Determine which values are not in sql table - sql

I have this query in oracle:
select * from table where col2 in (1,2,3,4);
lets say I got this result
col1 | col2
-----------
a 1
b 2
My 'in (1,2,3,4)' part has like 20 or more options, how can I determinate which values I don't found in my table? in my example 3 and 4 doesn't exist in the table

You can't in the way you want.
You need to insert the values you want to find into a table and than select all the values which don't exist in the desired table.
Lets say the data you want to find is in A and you want to know which doesn't exist in B.
SELECT *
FROM table_a A
WHERE NOT EXISTS (SELECT *
FROM table_b B
WHERE B.col1 = A.col1);

IN lists are stupid, or at least not very useful. Use a SQL Type collection to store your values instead because we can turn them into tables.
In this example I'm using the obscure SYS.KU$_OBJNUMSET type, which is the only nested table of Number I know of on 10g. (There's lots more in 11g).
So
select t.column_value
from table ( SYS.KU$_OBJNUMSET (1,2,3,4) ) t
left join your_table
on col2 = t.column_value
where col2 is null;

Here would be a way to do it if you're just using integers for your specific example:
SELECT *
FROM (
Select Rownum r
From dual
Connect By Rownum IN (1,2,3,4)
) T
LEFT JOIN YourTable T2 ON T.r = T2.Col2
WHERE T2.Col2 IS NULL
And the Fiddle.
This creates a table out of your where criteria 1,2,3,4 and uses that to LEFT JOIN on.
--EDIT
Because values aren't ints, here is another "ugly" option:
SELECT *
FROM (
Select 'a' r From dual UNION
Select 'b' r From dual UNION
Select 'c' r From dual UNION
Select 'd' r From dual
) T
LEFT JOIN YourTable T2 ON T.r = T2.Col2
WHERE T2.Col2 IS NULL
http://www.sqlfiddle.com/#!4/5e769/2
Good luck.

Related

Inner joined same query returns more result than when it's executed alone

I don't know if I'm wrong but I've always thought (and I still do) that the number of records returned from querying a table alone and inner join the same table and querying this relation would be the same. Like this:
select 'foo' foo from dual;
versus
select * from (select 'foo' foo from dual)q1 inner join
(select 'foo' foo from dual)q2
on q1.foo=q2.foo
Both these queries return one record. But I have a query when I inner join it with itself I get more records . Here's my query:
SELECT distinct DOC.DOCID
FROM AG_INW_DOC DOC
JOIN LAG_CITIZENS CIT
ON DOC.CITIZENID=CIT.CITIZENID
JOIN
(SELECT TSK.DOCID,
OFCR.DEPID
FROM AG_TASKS TSK
JOIN AG_TASK_EXECUTORS EXEC
ON TSK.TASKID=EXEC.TASKID
JOIN AG_OFFICERS OFCR
ON EXEC.ISSUEDOFFICERID =OFCR.OFFICERID
WHERE EXEC.ISMAINEXECUTOR =1
)TSK ON DOC.DOCID =TSK.DOCID
LEFT JOIN
(SELECT ESCDOCID, UNDERCONTROL,ORGID FROM AG_ESCORTING_DOCUMENTS
) ESC
ON DOC.ESCDOCID =ESC.ESCDOCID
WHERE DOC.CATEGORYID IN (11,12)
AND TRUNC(DOC.RECEIVEDDATE,'DDD') BETWEEN TO_DATE('01.11.2015') AND TO_DATE('30.11.2015')
AND (TSK.DEPID IN ('017','004')
OR (TSK.DEPID ='008'
AND DOC.SUBJECTID IN (1,2,3,4,20,22,23,24) ))
AND DOC.DOCSTAT! =3
UNION ALL
SELECT distinct DOC.DOCID
FROM AG_INW_DOC DOC
JOIN LAG_CITIZENS CIT
ON DOC.CITIZENID=CIT.CITIZENID
LEFT JOIN AG_TASKS TSK
ON DOC.DOCID =TSK.DOCID
LEFT JOIN
(SELECT ESCDOCID, UNDERCONTROL,ORGID FROM AG_ESCORTING_DOCUMENTS
) ESC
ON DOC.ESCDOCID =ESC.ESCDOCID
WHERE DOC.CATEGORYID IN (11,12)
AND DOC.ADDRESSEDOFFICERID IN (9,26)
AND TRUNC(DOC.RECEIVEDDATE,'DDD') BETWEEN TO_DATE('01.11.2015') AND TO_DATE('30.11.2015')
AND DOC.DOCSTAT! =3
If I run this query alone I get 3019 records returned. But if I inner join it with itself and select from this join I get 3023 records.
Now, I don't expect anyone to examine my query and point out the problem. I just need to know what circumstances might cause this behavior.
EDIT
The query returns only distinct values. No duplicates
Your assumption is wrong.
an inner join will combine every result from the first select with every result from the second select, and then filter for the join condition. So if your select returns a single column with the following three values:
1, 2, 2
A join with itself and the join condition that the values must be the same will yield
So you get 5 rows instead of 3.
Without looking at your actual select you probably have non-unique values in the columns of you join condition.
(1, 1), (2, 2), (2, 2), (2, 2), (2, 2)
In order to find the duplicates wrap you complete query in something like this
select DOCID, count(*) from (
-- your query here
) group by DOCID
having count(*) > 1
Your assumption holds only if you join on a primary or a unique key.
Here a small example that demonstrates the opposite:
This query gives two rows, but the key is not unique:
select 'foo, I''M no PK' foo from dual union all
select 'foo, I''M no PK' foo from dual
;
Join of the above row source (using WITH) give 2 * 2 rows.
with dual2 as (
select 'foo, I''M no PK' foo from dual union all
select 'foo, I''M no PK' foo from dual
)
select * from (select foo from dual2)q1 inner join
(select foo from dual2)q2
on q1.foo=q2.foo
;
.
foo, I'M no PK foo, I'M no PK
foo, I'M no PK foo, I'M no PK
foo, I'M no PK foo, I'M no PK
foo, I'M no PK foo, I'M no PK
UPDATE
The above assumption is valid, but not relevant for this question.
The problem is in the construction DISTINCT UNION ALL DISTINCT
This may pass dups - the UNION must be used instead.
Example
with tab1 as (
select 1 foo from dual union all
select 1 foo from dual union all
select 2 foo from dual)
, tab2 as (
select 2 foo from dual union all
select 2 foo from dual union all
select 3 foo from dual)
select DISTINCT foo from tab1
UNION ALL
select DISTINCT foo from tab2
order by 1;
gives
1
2
2
3

Join two tables to get all data

Suppose I have two tables:
Table 1
Col
1
3
4
5
6
9
and Table 2
Col
2
4
6
8
How can I Merge the two tables so I have 1-9 and if a number only appears in one table, the corresponding position in the other table has a null? Thank you.
I'm assuming you want the numbers that actually exist in at least one of the tables, which won't give you a row with 7;
What you're looking for seems to be something like a FULL OUTER JOIN which works in pretty much any RDBMS except MySQL;
SELECT a.col col_a, b.col col_b
FROM Table1 a
FULL OUTER JOIN Table2 b ON a.col = b.col
ORDER BY COALESCE(a.col, b.col);
An SQLfiddle to test with.
Sadly, MySQL does not have FULL OUTER JOIN, so you'll have to do the same operation using a UNION between a LEFT JOIN and a RIGHT JOIN;
SELECT * FROM (
SELECT a.col col_a, b.col col_b
FROM Table1 a
LEFT JOIN Table2 b ON a.col = b.col
UNION
SELECT a.col col_a, b.col col_b
FROM Table1 a
RIGHT JOIN Table2 b ON a.col = b.col
)z
ORDER BY COALESCE(col_a, col_b);
Another SQLfiddle.
If i am not wrong. you need records from both table. (please correct me if i am wrong)
Try following to get data from both tables:
select col from Table1
union
select col from Table2
select col from Table1
union all
select col from Table2
NOTE:
UNION removes duplicate records (where all columns in the results are the same), UNION ALL does not.
There is a performance hit when using UNION vs UNION ALL, since the database server must do additional work to remove the duplicate rows, but usually you do not want the duplicates (especially when developing reports).
You can try this one using union:
SELECT * FROM (SELECT col AS value FROM table1
UNION
SELECT col AS value FROM table2)t1
ORDER BY value
try and post your comments:
Thanks

select first N distinct rows without inner select in oracle

I have something like the following structure: Table1 -> Table2 relationship is 1:m
I need to perform queries similar to the next one:
select Table1.id from Table1 left outer join Table2 on (Table1.id1 = Table2.id2) where Table2.name like '%a%' and rownum < 11
i.e. I want first 10 ids from Table 1 which fulfils conditions in Table2. The problem is that I've to use distinct, but the distinct clause applies after 'rownum < 11', so the result could be e.g. 5 records even if their number is more than 10.
The apparent solution is to use the following:
select id from ( select Table1.id from Table1 left outer join Table2 on (Table1.id1 = Table2.id2) where Table2.name like '%a%' ) where rownum < 11
But I'm afraid of performance of such a query. If Table1 contains about 300k records, and Table2 contains about 700k records, wouldn't such a query be really slow?
Is there another query, but without inner select? Unluckily, I want to avoid using inner selects.
Unluckily, I want to avoid using inner
selects
With having the WHERE clause on TABLE2, you are filtering the select to an INNER JOIN (ie. since Table2.name IS null <> Table2.name like '%a%' you will only get results where the join is INNER to one another. Also, the %a% without a function based index will result in a full table scan on each iteration.
but #lweller is completely correct, to do the query correctly you will need to use a subquery. keep in mind, without an ORDER BY you have no guarantee of the order of your top X records (it may always 'appear' that the values conform to the primary key or whatnot, but there is no guarantee.
WITH TABLE1 AS(SELECT 1 ID FROM DUAL
UNION ALL
SELECT 2 ID FROM DUAL
UNION ALL
SELECT 3 ID FROM DUAL
UNION ALL
SELECT 4 ID FROM DUAL
UNION ALL
SELECT 5 ID FROM DUAL) ,
TABLE2 AS(SELECT 1 ID, 'AAA' NAME FROM DUAL
UNION ALL
SELECT 2 ID, 'ABB' NAME FROM DUAL
UNION ALL
SELECT 3 ID, 'ACC' NAME FROM DUAL
UNION ALL
SELECT 4 ID, 'ADD' NAME FROM DUAL
UNION ALL
SELECT 1 ID, 'BBB' NAME FROM DUAL
) ,
sortable as( --here is the subquery
SELECT
Table1.ID ,
ROW_NUMBER( ) OVER (ORDER BY Table2.NAME NULLS LAST) ROWOverName , --this wil handle the sort
table2.name
from
Table1
LEFT OUTER JOIN --this left join it moot, pull the WHERE table2.name into the join to have it LEFT join as expected
Table2
on
(
Table1.id = Table2.id
)
WHERE
Table2.NAME LIKE '%A%')
SELECT *
FROM sortable
WHERE ROWOverName <= 2;
-- you can drop the ROW_NUMBER( ) analytic function and replace the final query as such (as you initially indicated)
SELECT *
FROM sortable
WHERE
ROWNUM <= 2
ORDER BY sortable.NAME --make sure to put in an order by!
;
You don't need DISTINCT here at all, and there is nothing bad in subqueries as such.
SELECT id
FROM Table1
WHERE id IN
(
SELECT id
FROM Table2
WHERE name LIKE '%a%'
)
AND rownum < 11
Note that the order is not guaranteed. To guarantee order, you have to use a nested query:
SELECT id
FROM (
SELECT id
FROM Table1
WHERE id IN
(
SELECT id
FROM Table2
WHERE name LIKE '%a%'
)
ORDER BY
id -- or whatever else
)
WHERE rownum < 11
There is no way to do it without nested queries (or the CTE).
For me there is no reason to be afraid of performance. I think the sub select ist the best way to solve your problem. And if you want don't trust me, take a look at explain plan of your query and you will see that it behave not so bad as you might think.

Can you define values in a SQL statement that you can join/union, but are not stored in a table outside of the statement?

I'm trying to create a query and need to join against something that I can define values in without creating a table.
I'll attempt to describe what I'm trying to do:
table1
-------
fieldA
is joined on fieldA with
table2
-------
(titles for FK in table 1)
Table1 has values outside of what exists in table2
I want to add an additional 'table' to be unioned with table2 and then joined with table 1
Thanks
Sure, you can use a UNION ALL inside a subselect and join with the result of that. Something like this might do the trick:
SELECT *
FROM table1 T1
JOIN (
SELECT titles, stuff
FROM table2
UNION ALL
SELECT 'foo' AS titles, 'foostuff' AS stuff
UNION ALL
SELECT 'bar' AS titles, 'barstuff' AS stuff
) T2
ON T1.id = T2.titles
Note that the columns in the UNION ALL must be of the same type and in the same order. The column names don't have to match though.
Looks like you want to add arbitrary results to your query?
select
id,
titles
from
table1 t1
inner join table2 t2
on t2.titles = t1.titles
union (
(select 100, 'Dogs' from dual)
union
(select 200, 'Pigs' from dual)
union
(select 300, 'Sheep' from dual)
)
That's an oracle flavour, for other RDBMS' there will be an equivalent to dual
If you're using a modern Oracle version, there is an even neater solution
WITH arbitrary_data AS (
SELECT 100 id, 'Dogs' titles FROM DUAL
UNION ALL SELECT 200, 'Pigs' FROM DUAL
UNION ALL SELECT 300, 'Sheep' FROM DUAL
)
SELECT
id,
titles
FROM
table1 t1
inner join table2 t2
on t2.titles = t1.titles
inner join arbitrary_data ad
on ad.titles = t1.titles

mysql - union tables by unique field

I have two tables with the same structure:
id name
1 Merry
2 Mike
and
id name
1 Mike
2 Alis
I need to union second table to first with keeping unique names, so that result is:
id name
1 Merry
2 Mike
3 Alis
Is it possible to do this with MySQL query, without using php script?
This is not a join (set multiplication), this is a union (set addition).
SELECT #r := #r + 1 AS id, name
FROM (
SELECT #r := 0
) vars,
(
SELECT name
FROM table1
UNION
SELECT name
FROM table2
) q
This will select all names from table1 and combine those with all the names from table2 which are not in table1.
(
select *
from table1
)
union
(
select *
from table2 t2
left join table1 t1 on t2.name = t1.name
where t1.id is null
)
Use:
SELECT a.id,
a.name
FROM TABLE_A a
UNION
SELECT b.id,
b.name
FROM TABLE_B b
UNION will remove duplicates.
As commented, it all depends on what your 'id' means, cause in the example, it means nothing.
SELECT DISTINCT(name) FROM t1 JOIN t2 ON something
if you only want the names
SELECT SUM(something), name FROM t1 JOIN t2 ON something GROUP BY name
if you want to do some group by
SELECT DISTINCT(name) FROM t1 JOIN t2 ON t1.id = t2.id
if the id's are the same
SELECT DISTINCT COALESCE(t1.name,t2.name) FROM
mytable t1 LEFT JOIN mytable t2 ON (t1.name=t2.name);
will get you a list of unique names from the 2 tables. If you want them to get new ids (like Alis does in your desired results), that's something else and requires the answers to a couple of questions:
do any of the names need to maintain their previous id. And if they do, which table's id should be preferred?
why do you have 2 tables with the same structure? ie what are you trying to accomplish when you generate the unique name list?