SQL advanced sub select query - sql

I would like to expand on this simple sub select:
Select * from table1 where pkid in (select fkid from table2 where clause...)
The logic above is fairly simple - get me all rows in table1 where the pkid is contained in the subset returned from the sub select query that has a where clause. It works well because there is only 1 field being returned.
Now I want to expand on this.
In table 1 I want to return results where field1 and field2 and field3 in select (field1, field2, field3 from table2 where clause...)
How is this possible?
Thanks in advance.
Example.
TABLE1
FIELD1 FIELD2 FIELD3
1 2 3
2 3 4
4 5 6
TABLE 2
2 3 4
4 5 6
I want to return 2 results.

If I understand what you need you can try:
SELECT t1.field1, t1.field2, t1.field3 FROM table1 t1
INNER JOIN table2 t2
ON t1.field1 = t2.field1
AND t1.field2 = t2.field2
AND t1.field3 = t2.field3
AND t2.... // Use this as WHERE condition

Like Marco pointed out, what you want to do is an INNER JOIN.
But (that's just FYI, you should definitely use Marco's solution) it's also possible to simply use braces.
Select *
from table1
where (field1, field2, field3) in (select field1, field2, field3 from table2 where clause...)
At least in MySQL (wasn't this question tagged with MySQL?)

you can use a temporary table
select field1, field2, field3 into #tempTable from table2 where clause...
select * from table 1
where filed1 in (select field1 from #tempTable)
and filed2 in (select field2 from #tempTable)
and filed3 in (select field3 from #tempTable)

Avoid using IN for most cases like this. It's very limitting.
I prefer to use a JOIN in most cases.
SELECT
*
FROM
yourTable
INNER JOIN
(SELECT c1, c2, c3 FROM anotherQuery) AS filter
ON yourTable.c1 = filter.c1
AND yourTable.c2 = filter.c2
AND yourTable.c3 = filter.c3
(Ensure the filter returns unique combinations of c1, c2, c3 using DISTINCT or GROUP BY if necessary)

you didn't mentioned engine, so I'll assume SQL Server.
This query will show you what's on both tables
select FIELD1, FIELD2 from table1
intersect
select FIELD1, FIELD2 from table2

Related

How to add 2 columns to existing query result based on another query?

I have a query like this:
SELECT
field1 as field1 ,
field2 as field2 ,
(select count(*) from ... where ...=field1) as field3
FROM
...
And it works fine - and I see 3 columns in results
The I need to add one more column for internal query:
SELECT
field1 as field1 ,
field2 as field2 ,
(select count(*) as my_count, sum(*) as my _sum from ...where ...=field1 ) as field3
FROM
...
this syntax doesn't work.
How can I achieve it ?
This partial query makes it unsure what you really want, but I would expect that the subquery actually correlates to the outer query (otherwise, you could just cross join). If so, a typical solution is a lateral join.
In Postgres:
select
field1 as field1,
field2 as field2,
x.*
from ...
left join lateral (
select count(*) as my_count, sum(*) as my _sum from ...
) x
Oracle supports lateral joins starting version 12. You just need to replace left join lateral with outer apply.
The following would seem to do what you want, and it should work fine in Oracle 9i:
SELECT t.field1,
t.field2,
x.my_count,
x.my_sum
FROM SOME_TABLE t
LEFT OUTER JOIN (select FIELD1,
count(*) as my_count,
sum(SOME_FIELD) as my_sum
from SOME_OTHER_TABLE
GROUP BY FIELD1) x
ON x.FIELD1 = t.FIELD1
You can use a CTE (Common Table Expression) to precompute the values:
WITH
q as (select count(*) as my_count, sum(*) as my _sum from ... )
SELECT
field1 as field1 ,
field2 as field2 ,
q.my_count as field3,
q.my_sum as field4
FROM
...
CROSS JOIN q
Or... you can always use the less performant, simpler way:
SELECT
field1 as field1 ,
field2 as field2 ,
(select count(*) from ... ) as field3,
(select sum(*) from ... ) as field4
FROM
...
With your limited (& a bit confusing - 2 databases, sum(*) ...) info,
here is the logic:
SELECT
field1 as field1 ,
field2 as field2 ,
(select count(*) from ... ) as my_count,
(Select sum(<my field>) from ...) as my _sum
FROM
...

Oracle SQL - Define table names for later usage?

I wanted to know if there is a way, in SQL Oracle, to do some range-definition (like in Excel). For example:
DEFINE TABLE1 = SELECT FIELD1, FIELD2, FIELD3 FROM [SCHEMA].[TABLE0][WHERE/GROUP BY/HAVING/ORDER BY/...];
DEFINE TABLE2 = SELECT FIELD1, FIELD2, FIELD3 FROM TABLE1 [WHERE/GROUP BY/HAVING/ORDER BY/...];
DEFINE TABLE3 = SELECT FIELD1, FIELD2, FIELD3 FROM TABLE2 LEFT JOIN TABLE1 ON [CONDITIONS];
SELECT * FROM TABLE3;
Thanks a lot in advance.
Based on your examples, it sounds like you want to create views:
CREATE VIEW TABLE1 AS
SELECT FIELD1, FIELD2, FIELD3
FROM [SCHEMA].[TABLE0][WHERE/GROUP BY/HAVING/...];
CREATE VIEW TABLE2 AS
SELECT FIELD1, FIELD2, FIELD3
FROM TABLE1 [WHERE/GROUP BY/HAVING/...];
CREATE VIEW TABLE3 AS
SELECT FIELD1, FIELD2, FIELD3
FROM TABLE2
LEFT JOIN TABLE1 ON [CONDITIONS];
SELECT * FROM TABLE3;
TO close this question. From one of the comments (Steve), what I needed is a WITH clause, as I didn't have DDL privileges.
Thanks,

How to use a field name as an argument for a nested select statement

I'm trying to do something wonky, but I cant think of any other way to do it:
SELECT
my_table.field1 as field1,
(EXISTS (SELECT 1 FROM another_table WHERE id = field1)) as does_exist
FROM my_table
This obviously fails because field1 doesn't exist at the time the result set is created. Does anyone know how to accomplish this?
You can use left join:
select distinct on (t1.field1) t1.field1, t2.id is not null as does_exist
from my_table t1
left join another_table t2 on t2.id = t1.field1
however your query should work as well:
SELECT
my_table.field1 as field1,
(EXISTS (SELECT 1 FROM another_table WHERE id = my_table.field1)) as does_exist
FROM my_table
SELECT
my_table.field1 as field1,
case another_table.ID
when null then 0
else 1
end does_exist
FROM my_table
left outer join another_table on another_table.ID = my_table.field1
Here 1 is Exists, and 0 not exists

Oracle NOT IN does not work will nullable fields

I have to compare two tables with identical structure (int not null,int not null, varchar2). In both tables field3 is a nullable.
I have the next SQL:
Select
t1.field1, t1.field2, t1.field3)
From
table1 t1
Where (field1,field2,field3)
not in
(select field1,
field2,field3
from table2 t2)
When the field3 is NULL in any of them (t1 or t2) the query does not return any row. For instance I want to return a row from this data but it returns nothing at all.
Table 1
field1 field2 field3
1 2 <NULL>
Table 2
field1 field2 field3
1 2 'some text data'
There is workaround to fix such thing by using the NVL function: NVL(field3, 'dummytextorwhatever') but I dont want to include such horrible thing into my code. Any idea to solve this problem with nullable fields?
Thank you!
This is known behavior with NOT IN when there are nulls in either the main table or the sub-query's result sets. As #DrCopyPaste puts it so well
"when writing WHERE value NOT IN (x, y, z) this will be internally interpreted as WHERE value != x AND value != y AND value != z, and comparing against NULL (either for equality or unequality) always yields FALSE"
The simple answer is to use NOT EXISTS:
Select
t1.field1, t1.field2, t1.field3)
From
table1 t1
Where not exists
(select null from table2 t2
where t2.field1 = t1.field1
and t2.field2 = t1.field2
and t2.field3 = t1.field3 )
An anti-join will produce the same result
Select
t1.field1, t1.field2, t1.field3)
From
table1 t1
left join table2 t2
on t2.field1 = t1.field1
and t2.field2 = t1.field2
and t2.field3 = t1.field3
where t2.field1 is null
"why do you select a null at the beginning?"
Because with NOT EXISTS it doesn't matter what the sub-query returns. All that matters is that it returns a non-empty result set. It could have been 1 or field1 but it really doesn't matter, so why not null?
Try to use NVL or Coalesce operators, like this
Select
t1.field1, t1.field2, t1.field3
From
table1 t1
Where (nvl(field1,0),nvl(field2,0),nvl(field3,0))
not in
(select nvl(field1,0),nvl(field2,0),nvl(field3,0)
from table2 t2)
but if in tables data there is some data equals 0 select will be return that row, because nvl(field1,0)=nvl(field2,0) when field1=0 and field2=null, so you can use any value(you should be confident
) wich no exists in your tables data for example -99(nvl(field,-99))
or you can use exists/not exists
Try not exists
Select
t1.field1,
t1.field2,
t1.field3
From
table1 t1
where not exists
(select 1
from table2 t2
where
t1.field1=t2.field1
and t1.field2=t2.field2
and t1.field3=t2.field3
)
Sample test
with table1(field1,field2,field3) as
(select 1,2,null from dual),
table2(field1,field2,field3) as
(select 1,2,'something' from dual)
Select
t1.field1,
t1.field2,
t1.field3
From
table1 t1
where not exists
(select 1
from table2 t2
where
t1.field1=t2.field1
and t1.field2=t2.field2
and t1.field3=t2.field3
)
Output
FIELD1 FIELD2 FIELD3
1 2
Based on your query, you are trying to find all the times in table1 that do not exist in table2. Instead of NOT IN, consider using a MINUS...
Select t1.field1, t1.field2, t1.field3
From table1 t1
Minus
select t2.field1, t2.field2, t2.field3
from table2 t2;

Transfer data from temp table to physical table

I have this table named table1
id uniquefield field1 field2
1 11 test test2
2 12 test2 test3
and I have this value in my temp table #temp1
id uniquefield field1 field2
1 11 test test2
2 12 test2 test3
3 13 test4 test5
4 14 test5 test6
Now, what I want to happen is that I want to transfer all data from #temp1 table. It would insert if data does not exist in table1 table and would update if it exist.
Does anybody know how to do this using SQL Server or dynamic SQL?
Hope to find some response from you.
Temp tables are no different in such cases like you mentioned. The difference is they are only available to the current connection for the user; and they are automatically deleted when the user disconnects from instances. So you can handle these tables like any other SQL table and use a MERGE query to achieve this data manupulation.
Assuming the uniquefield column can be treated as link between these tables.
MERGE table1 t
USING #temp1 t1
ON t.uniquefield = t1.uniquefield
WHEN MATCHED THEN
UPDATE
SET t.id = t1.id,
t.field1 = t1.field1,
t.field2 = t1.field2
WHEN NOT MATCHED BY TARGET THEN
INSERT (id, uniquefield, field1, field2)
VALUES (t1.id, t1.uniquefield, t1.field1, t1.field2 );
You can DROP #temp1 after this and do a SELECT * FROM table1 to check the updated/ inserted data.
Assume 2 table row is identical by id, insert with not exists()
-- Append missing row to table1
INSERT table1
SELECT * FROM #temp1 t WHERE NOT EXISTS(SELECT * FROM table1 WHERE id = t.id)
Temp tables are no different in such cases like you mentioned. The difference is they are only available to the current connection for the user; and they are automatically deleted when the user disconnects from instances. So you can handle these tables like any other SQL table.
Assuming the uniquefield column can be treated as link between these tables.
Update statemant:
update table1
set
t.id = t1.id,
t.field1 = t1.field1,
t.field2 = t1.field2
from table1 t
join #temp1 t1
on t.uniquefield = t1.uniquefield
Insert statement:
insert into table1(id, uniquefield, field1, field2)
select t1.id, t1.uniquefield, t1.field1, t1.field2
from table1 t
join #temp1 t1
on t.uniquefield != t1.uniquefield
This is assuming that id is the table primary key. Otherwise replace appropriately.
UPDATE Table1
SET id= T.id,
Uniquefield = T.Uniquefield,
Field1 = T.field1,
Field2 = T.field2
FROM Table1
INNER JOIN #temp1 T ON T.id = Table1.id;
INSERT INTO Table1 (id, uniquefield, field1, field2)
SELECT id, uniquefield, field1, field2
FROM #temp1
WHERE id NOT IN (SELECT id FROM Table1)
I think the elegant way is to use MERGE here:
SET IDENTITY_INSERT table1 ON;
MERGE INTO table1 AS T
USING #temp1 AS S
ON S.id = T.id
WHEN MATCHED THEN
UPDATE SET
T.uniquefield = S.uniquefield,
T.field1 = S.field1,
T.field2 = S.field2
WHEN NOT MATCHED BY TARGET THEN
INSERT (id, uniquefield, field1, field2)
VALUES (S.id, S.uniquefield, S.field1, S.field2);
SET IDENTITY_INSERT table1 OFF;
I have added the IDENTITY_INSERT there just in case ID column in your table1 is an IDENTITY and you might want to keep the one from #temp1 table. If you dont need / have IDENTITY, just remove those lines.
you can simply use this query:-
insert into table1
select * from (
select #temp.workersID,#temp.W_name,#temp.salary,#temp.joining_year,#temp.city,#temp.id
from #temp
full join workers
on #temp.WorkersID = workers.WorkersID
where workers.WorkersID is null
) ds