Check if combination of fields in Table1 exists in another Table2 (SQL) - sql

I need to check whether a combination of values in my table A exists in the specified corresponding set of columns in a different table, B.
So far, I'm doing this, which doesn't seem very elegant or efficient:
select * from table1 where
colX_table_1 NOT IN (select colX_table_2 from table2)
and
colY_table_1 NOT IN (select colY_table_2 from table2)
Is there a better/faster way to do this combination check (colX_table_1,colY_table_1) -> (colX_table_2,colY_table_2)?

The query you gave evaluates each field separately:
select * from table1 where
colX_table_1 NOT IN (select colX_table_2 from table2)
and
colY_table_1 NOT IN (select colY_table_2 from table2)
This is not merely unelegant, as you claim, it is wrong, as it does not enforce combinations. E.g., consider the following tables:
table1:
------
colX colY
1 1
table2:
------
colX colY
1 2
2 1
According to your post, you are looking for a query that would return the row in table1, since such a combination does not exist in table2. However, in the given query, each part of the where clause evaluates to false, and the row is not returned.
Instead, in order to check the combination and not each column individually, you could use an exists condition:
SELECT *
FROM table1
WHERE NOT EXISTS (SELECT *
FROM table2
WHERE table1.colx = table2.colx AND
table1.coly = table2.coly)

It depends a bit on your data but this worked in my case:
select *
from table1
where colx||coly not in (select colx||coly from table2)
This notation is just a string concatenation: ||

Related

Choose which query will be used / Conditional query in Oracle

I have two queries, query1 and query2. What I would like to do is, if returned rows of query1 is empty, return query2 instead. Is that possible using basic SQL query alone? They have same returning columns btw, but different table sources.
eg:
query1:
SELECT name, message
FROM table1
query2:
SELECT name, message
FROM table 2
If query1 is empty, return name, message from query2.
This will select from table1 if not empty, otherwise select from table2:
SELECT * FROM table1
WHERE EXISTS (select * from table1)
UNION ALL
SELECT * FROM table2
WHERE NOT EXISTS (select * from table1)
This checks if table1 has no rows:
EXISTS (SELECT * FROM TABLE)
Found an answer here: https://stackoverflow.com/a/25122516/3747493
Basically:
SELECT *
FROM table1
UNION ALL
SELECT *
FROM table2
WHERE (SELECT COUNT(*) FROM table1) = 0
Thanks guys!

check if all table rows are equal

There are two tables with identical structure.
Let's assume the number of rows in both is equal.
how would you check if all rows are equal? Is there any faster way than comparing every column value of a given row with the same id in both tables?
Try this:
SELECT * FROM table1
EXCEPT
SELECT * FROM table2
If anything is returned then they are not equal.
Abe's answer is correct, but only if they have the same number of rows. (I misread the question when I wrote my original response "condemning" his answer.) If table1 may be a subset of (a larger) table2 or vice versa, I would try:
if
(
not exists ( select * from table1
except
select * from table2
)
and
not exists ( select * from table2
except
select * from table1
)
)
This gives true if they are the same, and false if they are different.

t-sql - delete second value only

i would like to run a sql statement that will delete ONLY the second value for example
delete from table1 where condition1
i want this statement to delete ONLY the second value
how can i accomplish this?
i would like to clarify. i have a field called field1 which is an autonumber and and it is a primary key and it increments. i would like to delete the record containing the greater number
You could also employ the ROW_NUMBER() function of SQL server to number each row, and use this number to isolate just the second item for deletion, according to your own custom ordering in the inner query ( over (ORDER BY <myKey> asc) ). This provides a great deal of flexibility.
DELETE a FROM table1
FROM table1 a
JOIN (
select ROW_NUMBER() over (ORDER BY <myKey> asc) as AutoNumber, <myKey> from table1
) b on a.<myKey> = b.<myKey>
WHERE condition1
AND b.AutoNumber = 2
Do you want to delete only the last duplicate, or all but the first?
For all but the first: (Edited to use CTE per #Martin's suggestion.)
with target as (select * from table1 where condition1)
delete from target goner
where exists (select * from target keeper
where keeper.field1 < goner.field1)
In other words, if there is another matching record with a lower field1, delete this record.
EDIT:
To delete only the last:
with target as (select * from table1 where condition1)
delete from target goner
where exists (select * from target keeper
where keeper.field1 < goner.field1)
and not exists (select * from target missing
where missing.field1 > goner.field1)
In other words, if there is another matching record with a lower field1, AND there is no matching record with a higher field1, then we have the highest duplicate, so nuke it.
It's been a while (so my syntax my not quite be right), and this may not be the best solution, but the "academic" answer would be something like:
delete from table1 where condition1
and field1 = (select max(field1) from table1 where condition1)
Try this:
DELETE MyTable
FROM MyTable
LEFT OUTER JOIN (
SELECT MIN(id) as id, Col1, Col2, Col3
FROM MyTable
GROUP BY Col1, Col2, Col3
) as KeepRows ON
MyTable.id= KeepRows.id
WHERE
KeepRows.RowId IS NULL
UPDATE
While this might not be as "pretty" as #Jeffrey's it works. From what I can tell, #Jeffrey's does not. See sql below (Delete replaced with SELECT * for demonstration):
WITH TEMP as
(
SELECT 1 as id,'A' as a,'Z' as b
UNION
SELECT 2,'A','Z'
UNION
SELECT 3,'B','Z'
UNION
SELECT 4,'B','Z'
)
SELECT *
FROM TEMP
LEFT OUTER JOIN (
SELECT MIN(id) as id, a, b
FROM TEMP
GROUP BY a, b
) as KeepRows ON
temp.id= KeepRows.id
WHERE
KeepRows.id IS NULL

MySQL UNION print every other

How can I make a sql query like the following:
(SELECT `name` FROM table1)
UNION
(SELECT `name` FROM table2)
return each other. Ie. name1 from table1, name1 from table2, name2 from table1, name2 from table2 and so on?
Edit:
Table1
name
Hello
world
Table2
name
guten
tag
The output wanted:
Hello
guten
world
tag
and this should also be possible if adding more unions, so that it takes from the first union, then the second, third, forth and so on.
You could number rows with variables, using 2,4,6,... for the first part of the union and 3,5,7,... for the second:
select #rownum1 := #rownum1+2 as rownum, name
from (select #rownum1:=0) r, table1
union all
select #rownum2 := #rownum2+2 as rownum, name
from (select #rownum2:=1) r, table2
order by rownum;
The select in the from clause (select #rownum2:=1) r is only used to initialize the variable.
Does this work?
set #i = 0;
set #j = 1;
select #i:=#i+2 as rownumber,
name
from table1
union
select #j:=#j+2 as rownumber,
name
from table2
order by rownumber
I read your question as wanting to alternate one row from table1, one from table2 and so on in your results set.
Edit in light of your edit:
Change the "2"s to the number of tables, and add as many variables as you have tables, with consecutive start values in the "set" statements. This will extend in the way you want.
In a similar vein to David M, you can do:
(SELECT #rownum:=#rownum+1 as rownum,`name` FROM table1, (SELECT #rownum:=0) r)
UNION
(SELECT #rownum:=#rownum+1 as rownum,`name` FROM table2, (SELECT #rownum:=0) r)
order by rownum

SQL "In" Statement Match Anything

If I have a query like this
SELECT * FROM table1 WHERE col1 IN ({SUBS})
Is there anything I can replace {SUBS} with that will return all rows in the table?
Further details:
I am building the SQL dynamically in my app, so I cannot (should not) edit other parts of the query except what's in braces. So,
SELECT * FROM table1
will not do.
Also,
SELECT * FROM table1 WHERE col1 IN (SELECT col1 FROM table1)
would be hackish and highly inefficient. Consider the table have more than 50k rows.
This would do it:
select col1 from table1
Edit: There seems to be a bit of confusion - the OP asked what value could be used to replace {SUBS} that would return all rows from table1. My answer above is what you could use in place of {SUBS} that would return all the rows.
This works for me in SQL Server:
SELECT * FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME IN (COLUMN_NAME)
Have you tried just using COL1 for {SUBS}?
e.g.
SELECT * FROM table1 WHERE col1 IN (col1)
If you replaced {SUBS} with SELECT col1 FROM table1, you would end up with
SELECT * FROM table1 WHERE col1 IN (SELECT col1 FROM table1);
which would return all rows from table1. This is, of course, simply a more roundabout way of saying:
SELECT * FROM table1;
You're right,
SELECT * FROM table1 WHERE col1 IN (SELECT col1 FROM table1)
does work, but is highly inefficient; requiring a merge join to return all rows.
Use the following which is just as efficient as regular SELECT * FROM table1
SELECT * FROM table1 WHERE col1 IN (col1)
However, that said; I suggest you have a chat to the person who is trying to impose the SELECT * FROM table1 WHERE col1 IN ({SUBS}) structure. There is no good reason to do so.
It unnecessarily complicates queries.
Creates risk of highly inefficient queries.
Potentially even limits developers to use certain techniques.
I suspect the person imposing this is trying to implement some sort of silver-bullet framework. Remember, the golden rule in software development is that there are no silver-bullets.
If you're simply trying to retrieve every row in the table, then:
select * from table1
If you're trying to prove a point or win a bet or something, then:
select * from table1 where col1 in (select col1 from table1)
If the query requires some WHERE condition, then I would try to replace it with an EXISTS statement:
select
*
from
table1 t1
where
exists ( {subs} )
Then {subs} can be replaced with any expression that does not yield NULL.
This works in Oracle:
select * from table1 where col1 in (col1)