Display records not matching in where clause - oracle11g

I need to display records not matching the where clause.
Example - select * from citytable where city in ('aa','bb','cc', 'dd','ee');
only aa, bb, cc are present in table, dd & ee are not present in the table. However, I still need to display dd & ee.

You are probably looking for something like this. An IN condition is the same as an inner join to a table containing the unique (distinct) values from the IN list. What you want is an outer join. You need to have a table instead of the IN list. In the solution below I show how you can create this "helper" table on the fly; there are several other methods, this just demonstrates the idea.
select deptno, ename from emp where deptno in (10, 50, 80);
DEPTNO ENAME
------ ------
10 CLARK
10 KING
10 MILLER
with h ( deptno ) as (
select 10 from dual union all
select 50 from dual union all
select 80 from dual
)
select h.deptno, e.ename
from h left outer join emp e
on h.deptno = e.deptno
;
DEPTNO ENAME
------ ------
10 CLARK
10 KING
10 MILLER
50
80

I'm not sure exactly how you want the output to look. If there is no data where city='ee', what exactly do you want to show? Something like this?
SELECT * FROM
(SELECT key AS A_key, one AS A_one, two AS A_two FROM cityTable WHERE one='aa') AS A
JOIN
(SELECT key AS E_key, one AS E_one, two AS E_two FROM cityTable WHERE one='ee') AS E
ON A_key=E_key
...etc.
Edit: or maybe this is it:
SELECT city FROM (SELECT city, count(*) AS c FROM cityTable GROUP BY city) WHERE c = 0

As i understand you said the 'dd' amd 'ee' are not present in the table but you still need it so you can achieve it using a union all. But remember that the columns of the 'dd' and 'ee' rows will always be null since there is no records present in your citytable
SELECT ct.col1 AS city, ct.col2.....<all columns of your table>
from citytable ct
where city in ('aa','bb','cc')
UNION ALL
select 'dd' as city,null ,null.....<nulls as many times as the number of columns of your table>
from citytable ct1
UNION ALL
select 'ee' as city,null ,null.....<nulls as many times as the number of columns of your table>
from citytable ct2

Related

Error in using WITH clause and INTERSECT in SQLite

I have two SQL queries;
First one is:
with b as (select person_id from people where name='Ward Bond' and born=1903)
select title_id from b natural join crew;
Which is producing correct results and is OK.
Another one is:
with c as (select person_id from people where name='John Wayne' and born=1907)
select title_id from c natural join crew;
Which is also totally OK and producing correct results. As soon as I try to find the intersection of these two queries using the following query:
with b as (select person_id from people where name='Ward Bond' and born=1903) select title_id from b natural join crew
intersect
with c as (select person_id from people where name='John Wayne' and born=1907) select title_id from c natural join crew;
I get the error Error: near "with": syntax error
I'm using SQLite3. Can you please help me to find the problem? The thing I'm trying to get is straightforward; I want to have the intersection of these two temporary tables.
This is the correct syntax for SQLite:
select * from (
with b as (
select person_id
from people
where name='Ward Bond' and born=1903
)
select title_id from b natural join crew
)
intersect
select * from (
with c as (
select person_id
from people
where name='John Wayne' and born=1907
)
select title_id from c natural join crew
);
Another way to get the intersected rows:
with cte(name, born) as (values ('Ward Bond', 1903), ('John Wayne', 1907))
select c.title_id
from crew c natural join people p
where (p.name, p.born) in cte
group by c.title_id
having count(distinct p.person_id) = 2;
That's how I did it using view:
create view a as select person_id from people where name='Ward Bond' and born=1903;
create view b as select person_id from people where name='John Wayne' and born=1907;
with c as
(select title_id from a natural join crew
intersect
select title_id from b natural join crew)
select primary_title from c natural join titles;

How do I include all max values within a row?

I'm very new to learning SQL, I apologize if my question isn't completely accurate.
The question I'm trying to answer with this query is "What is the most popular music genre in each country?" I've had to use a subquery and it works, but I found that for a few countries in the table, more than one genre has the MAX value. I'm stuck with how to edit my query so that all genres with the max value show in the results. Here is my code, using DB Browser for SQLite:
SELECT BillingCountry AS Country , name AS Genre , MAX(genre_count) AS Purchases
FROM (
SELECT i.BillingCountry, g.name, COUNT(g.genreid) AS genre_count
FROM Invoice i
JOIN InvoiceLine il
ON il.InvoiceId = i.InvoiceId
JOIN TRACK t
ON il.trackid = t.TrackId
JOIN Genre g
ON t.genreid = g.GenreId
GROUP BY 1,2
) sub
GROUP BY 1
Here is an example of the result:
| Country | Genre |Purchase|
|---------|-------|--------|
|Agrentina| Punk | 9 |
|Australia| Rock | 22 |
BUT in running just the subquery to COUNT the purchases, Argentina has two Genres with 9 Purchases (the max number for that country). How do I adjust my query to include both and not just the first one in the row?
You can do it with RANK() window function:
SELECT BillingCountry, name, genre_count
FROM (
SELECT i.BillingCountry, g.name, COUNT(*) AS genre_count,
RANK() OVER (PARTITION BY i.BillingCountry ORDER BY COUNT(*) DESC) rnk
FROM Invoice i
INNER JOIN InvoiceLine il ON il.InvoiceId = i.InvoiceId
INNER JOIN TRACK t ON il.trackid = t.TrackId
INNER JOIN Genre g ON t.genreid = g.GenreId
GROUP BY i.BillingCountry, g.name
)
WHERE rnk = 1
This will return the ties in separate rows.
If you want 1 row for each country, you could also use GROUP_CONCAT():
SELECT BillingCountry, GROUP_CONCAT(name) AS name, MAX(genre_count) AS genre_count
FROM (
SELECT i.BillingCountry, g.name, COUNT(*) AS genre_count,
RANK() OVER (PARTITION BY i.BillingCountry ORDER BY COUNT(*) DESC) rnk
FROM Invoice i
INNER JOIN InvoiceLine il ON il.InvoiceId = i.InvoiceId
INNER JOIN TRACK t ON il.trackid = t.TrackId
INNER JOIN Genre g ON t.genreid = g.GenreId
GROUP BY i.BillingCountry, g.name
)
WHERE rnk = 1
GROUP BY BillingCountry

executing multiple select statements to pick a value in where condition using Case statement

Case statement with select statement as loops in where condition
need to bring values by referring two tables. if the value doesn't exist in table a it has to refer the 2nd table
Sel * from Table A
where city = (case when (sel distinct city from Table A) is null
then (sel city from Table B) end)
expected output is as shown below
Sel * from Table A
where City = 'XYZ'
if value is not present in table A it has to refer Table B statement and show the value in where condition
The one thing you need to be careful with here is to make sure you return a single value in your scalar sub-queries -- (sel distinct city from Table A) and (sel city from Table B). If you can always guarantee that then I think your query will work as is.
A safer way to do it is guarantee you always get one row. Here's one option:
SELECT *
FROM TableA
WHERE City = (
SELECT city
FROM (
-- Get all cities from TableA
SELECT city, 1 AS table_priority
FROM tableA
UNION ALL
-- Get all cities from TableB
SELECT city, 2
FROM tableB
) src
QUALIFY ROW_NUMBER() OVER(ORDER BY src.table_priority, src.city) = 1 -- Return one row
)

An SQL query to display the count of distinct values of a single column into a single row

There is a table "OBJECTS" with one column "OBJECT_NAME" having three values: OBJ1, OBJ2, OBJ3.
Snapshot of the table OBJECTS is:
OBJECT_NAME
----------
OBJ3
OBJ1
OBJ2
OBJ1
OBJ1
OBJ2
Need to write a most efficient SQL query to output the count of the distinct object in the format as shown below. Make sure that performances are optimised and explain what was done for the optimization.
OBJ1_Count OBJ2_COUNT OBJ3_COUNT
---------------------------------
3 2 1
Your homework assignment marks should be given to me once i illustrate the example for your query :P. Hope it helps.
SELECT *
FROM
(SELECT 'OBJ1' AS NM FROM DUAL
UNION ALL
SELECT 'OBJ1' AS NM FROM DUAL
UNION ALL
SELECT 'OBJ2' AS NM FROM DUAL
UNION ALL
SELECT 'OBJ2' AS NM FROM DUAL
UNION ALL
SELECT 'OBJ3' AS NM FROM DUAL
UNION ALL
SELECT 'OBJ2' AS NM FROM DUAL
UNION ALL
SELECT 'OBJ3' AS NM FROM DUAL
UNION ALL
SELECT 'OBJ3' AS NM FROM DUAL
) PIVOT ( COUNT (NM) FOR NM IN ('OBJ1','OBJ2','OBJ3') );
---------------------------------OUTPUT--------------------------------------
'OBJ1' 'OBJ2' 'OBJ3'
2 3 3
---------------------------------OUTPUT--------------------------------------

Pivot rows to columns based on content in Oracle 10g PL/SQL

I have a table in my database, user_answers that stores users answers to a series of questions, with rows; user_id, question_id, answer_id and text_entry. Question text and answer text (if any) are stored in lookup tables. There are three types of questions, single-answer questions, multiple-answer questions and text-entry answer questions. So a single user might have entries like the following in the user_answers table:
user_id question_id answer_id text_entry
------- ----------- --------- ----------
123 100 1010 (null)
123 200 2010 (null)
123 200 2030 (null)
123 300 3000 "code 789"
Lets say the questions_text table has:
question_id text
----------- -------------
100 "Gender"
200 "Interests"
300 "Your code"
and the answers_text table has:
answer_id text
--------- -----------
1010 "Female"
1020 "Male"
2010 "Sports"
2020 "Computers"
2030 "Movies"
3000 (null)
I want to extract the data into a csv with one line per user_id showing the answers, something like this:
User,Gender,Sports,Computers,Movies,Code
123,Female,1,0,1,code 789
I know how to generate the CSV file via SQLPlus (I only have access to the DB via SQLPlus for reasons beyond my control...) but I don't know how to generate the PL/SQL statement.
In PL/SQL I know I can generate a pivot of the Gender question by doing
SELECT
user_id || ',' ||
MIN(DECODE(question_id, '100', (SELECT text FROM answers_text where answer_id = answer_text.answer_id)))
FROM user_answers
GROUP BY user_id
ORDER BY user_id
;
(I'm not an SQL guy, so this is copied off the internets!)
This code is (at least as far as my testing is telling me) good for the single-answer questions but will not work on multiple-answer or text-entry type questions.
I saw some stuff online about using the case statement in PL/SQL like so:
MIN(CASE WHEN question_id = '200' AND answer_id = '2010' THEN '1' ELSE '0' END)
...but I can't figure out how to get the answers into columns. And all the SO questions I can find that might be related are sql-server specific.
Is there a way to generate my desired output from a single PL/SQL statement? Preferably written in a way that does not depend on the data in the tables as we have a number of databases that this might need to be run on.
To accomplish what you are looking for (and not be specific to this data) I believe you are going to need some extra fields in your tables. For example, you will need to know which questions are Single-Answer, Multi-Answer, and Text-Entry without having to look at the data. You will also need to know which answers are possible for your Multi-answer questions without having to link through the data. From there, you can loop through the meta information about each question / answer combination and build yourself a query to run that will return the data in your desired format. Something like:
/* Create Tables with Data - Note 2 new columns added to questions_text */
create table user_answers
as
select 123 user_id, 100 question_id, 1010 answer_id, null text_entry from dual
union all
select 123 user_id, 200 question_id, 2010 answer_id, null text_entry from dual
union all
select 123 user_id, 200 question_id, 2030 answer_id, null text_entry from dual
union all
select 123 user_id, 300 question_id, 3000 answer_id, 'code 789' text_entry from dual;
create table questions_text
as
select 100 question_id, 'Gender' question_text, 'S' question_type, 1000 answer_set_id from dual
union all
select 200 question_id, 'Interests' question_text, 'M' question_type, 2000 answer_set_id from dual
union all
select 300 question_id, 'Your code' question_text, 'T' question_type, 3000 answer_set_id from dual;
create table answers_text
as
select 1010 answer_id, 'Female' text, 1000 answer_set_id from dual
union all
select 1020 answer_id, 'Male' text, 1000 answer_set_id from dual
union all
select 2010 answer_id, 'Sports' text, 2000 answer_set_id from dual
union all
select 2020 answer_id, 'Computers' text, 2000 answer_set_id from dual
union all
select 2030 answer_id, 'Movies' text, 2000 answer_set_id from dual
union all
select 3000 answer_id, null text, 3000 answer_set_id from dual;
/* PL/SQL for creating SQL statement to return data in desired format */
declare
v_sql VARCHAR2(32767);
begin
v_sql := 'select ua.user_id "User",';
FOR question IN (
select question_id, question_text, question_type, answer_set_id
from questions_text
)
LOOP
IF question.question_type = 'M'
THEN
FOR answer IN (
select answer_id, text
from answers_text
where answer_set_id = question.answer_set_id
)
LOOP
v_sql := v_sql||chr(10)||'max(case when ua.question_id = '||question.question_id||' and ua.answer_id = '||answer.answer_id||' then 1 else 0 end) "'||answer.text||'",';
END LOOP;
ELSIF question.question_type = 'S'
THEN
v_sql := v_sql||chr(10)||'min(case when ua.question_id = '||question.question_id||' then at.text end) "'||question.question_text||'",';
ELSIF question.question_type = 'T'
THEN
v_sql := v_sql||chr(10)||'min(case when ua.question_id = '||question.question_id||' then ua.text_entry end) "'||question.question_text||'",';
END IF;
END LOOP;
v_sql := rtrim(v_sql,',');
v_sql := v_sql||' from
user_answers ua
inner join questions_text qt
on qt.question_id = ua.question_id
inner join answers_text at
on at.answer_id = ua.answer_id
group by
ua.user_id';
-- replace dbms_output with code to write file
dbms_output.put_line(v_sql);
END;
Queries with an unknown number of columns are problematic at best. Will you really not know what the data will look like? You might want to look at this Ask Tom response for a package which might help get you the results you need.

Resources