SQL select field causing major performance problem - sql

I have a stored procedure that joins in numerous tables and selects fields from them. One of the tables being a temporary table.
SELECT
a.Field1,
a.Field2,
b.Field3,
b.Field4,
c.Field5
FROM table1 a
LEFT JOIN #table2 b ON a.Field1 = b.Field1
INNER JOIN table3 c ON a.Field1 = c.Field1
The above takes 10+ minutes, however if I comment out the two b fields from the select while leaving the join in place it runs in just seconds.
I have pulled this out of procedure to simplify and same behavior. Also the execution plans are almost identical.
Any help is appreciated.

How many rows are in the temp table, and is "Field2" in the temp table a primary key?
If you're not selecting any rows from the right table of a left join, and the join is to the primary key (or possibly a unique key), and you reference no columns from the right table, SQL Server can avoid having to access the temp table at all (since the presence or absence of a joining row has no impact on the final result):
Example. Table setup:
create table T1 (
ID int not null primary key,
Col1 varchar(10) not null
)
go
insert into T1 (ID,Col1)
select 1,'a' union all
select 2,'b' union all
select 3,'c'
go
create table #t2 (
ID int not null primary key,
Col2 varchar(10) not null
)
go
insert into #t2 (ID,Col2)
select 1,'d' union all
select 2,'e' union all
select 4,'f'
go
create table #t3 (
ID int not null,
Col3 varchar(10) not null
)
go
insert into #t3 (ID,Col3)
select 1,'d' union all
select 2,'e' union all
select 1,'f'
And the queries:
select T1.ID,T1.Col1 from T1 left join #t2 t2 on T1.ID = t2.ID
select T1.ID,T1.Col1,t2.Col2 from T1 left join #t2 t2 on T1.ID = t2.ID
select T1.ID,T1.Col1 from T1 left join #t3 t3 on T1.ID = t3.ID
select T1.ID,T1.Col1,t3.Col2 from T1 left join #t2 t3 on T1.ID = t3.ID
In all but the first query, the join happens as expected. But because the presence or absence of rows in #t2 can't affect the final result for the first query, it avoids performing the join entirely.
But if it's not something like that (and I'd expect it to be an obvious difference in the query plans)< I#m a bit stumped.

Have you tried inverting the joins? (although you are missing a join condition for table c in the sample query)
SELECT
a.Field1,
a.Field2,
b.Field3,
b.Field4,
c.Field5
FROM table1 a
INNER JOIN table3 c
LEFT JOIN #table2 b ON a.Field1 = b.Field1

I would try adding an index with included columns to #table2 and see if it helps:
CREATE NONCLUSTERED INDEX IX_table2
ON #table2 (Field1)
INCLUDE (Field3, Field4);

How about running the query in two parts. Make the first part as restrictive as possible and then only outer join on the filtered set.
SELECT a.Field1,
a.Field2,
b.Field3,
c.Field5
INTO #t
FROM table1 a
INNER JOIN table3 c ON a.Field1 = c.Field1
SELECT t.Field1,
t.field2,
b.field3,
b.field4,
t.field5
FROM #t t
LEFT OUTER JOIN #table2 b ON t.Field1 = b.Field1

select * into #temp from table1
select * into #temp1 from table2
select * into #temp2 from table3
SELECT
a.Field1,
a.Field2,
b.Field3,
b.Field4,
c.Field5
FROM #temp a
LEFT JOIN #temp1 b ON a.Field1 = b.Field1
INNER JOIN #temp2 c ON a.Field1 = c.Field1
if(Object_Id('TempDB..#temp') Is Not Null)
Begin
Drop table #temp
End
if(Object_Id('TempDB..#temp1') Is Not Null)
Begin
Drop table #temp1
End
if(Object_Id('TempDB..#temp2') Is Not Null)
Begin
Drop table #temp2
End

Related

Get the list of name column values which are not common in both the tables?

recently i gave an interview where the question was
suppose there are two tables in database.
Table T1 has a column named "name" in it and few other columns
Table T2 also has a column name "name" and few other columns
suppose table T1 has values in name column as
[n1,n2,n3,n4,n5]
and values in the "name" column of table T2 are
[n2,n4]
then output should be
[n1,n3,n5] as n2 and n4 are common in both tables
we needs to find the list of names which are not common in both the tables.
The solution that i provided him was using join in the below form
select name from table1 where name not in (select t1.name from table1 t1 join table2 t2 on t1.name=t2.name)
UNION
select name from table2 where name not in (select t1.name from table1 t1 join table2 t2 on t1.name=t2.name)
But he said there is still a better solution. I was not able to come up with any different and more efficient solution. What is the other efficient way to get the list of names if there is any?
If the NAME column does not have NULL values, there is also
select distinct(coalesce(a.name, b.name)) name
from table1 a
full join table1 b on a.name = b.name
where a.name is null or b.name is null
(Corrected WHERE condition, sorry...)
Use FULL OUTER JOIN:
SELECT DISTINCT(COALESCE(t1.NAME, t2.NAME)) AS NAME
FROM TABLE1 t1
FULL OUTER JOIN TABLE2 t2
ON t2.NAME = t1.NAME
WHERE t1.NAME IS NULL OR
t2.NAME IS NULL
A FULL OUTER JOIN is similar to a LEFT OUTER JOIN unioned with a RIGHT OUTER JOIN - it returns rows where data exists in the first table but not the second, or where it data exists in the second table but not the first. You could get the same effect by using
SELECT t1.NAME
FROM TABLE1 t1
LEFT OUTER JOIN TABLE2 t2
ON t2.NAME = t1.NAME
WHERE t2.NAME IS NULL
UNION
SELECT t2.NAME
FROM TABLE1 t1
RIGHT OUTER JOIN TABLE2 t2
ON t2.NAME = t1.NAME
WHERE t1.NAME IS NULL
and in fact the above is what you'd need to do if you were using a database which doesn't support the FULL OUTER JOIN syntax (e.g. MySQL, the last time I looked).
See this dbfiddle
Union the tables and return the values that don't have a count of 2:
create table t1 (
c1 int
);
create table t2 (
c1 int
);
insert into t1 values ( 1 );
insert into t1 values ( 3 );
insert into t2 values ( 2 );
insert into t2 values ( 3 );
commit;
select c1 only_in_one_table
from (
select 'T1' t, c1 from t1
union
select 'T2' t, c1 from t2
)
group by c1
having count(*) <> 2;
ONLY_IN_ONE_TABLE
1
2
I'm not a fan of not in with subqueries, because it behaves unexpectedly with null values. And the person asking would have to explain what "better" means. Your version is actually reasonable.
I might be inclined to approach this using aggregation:
select name
from ((select distinct name, 1 as in_table1, 0 as in_table2
from table1
) union all
(select distinct name, 0 as in_table1, 0\1 as in_table2
from table2
)
) t
group by name
having max(in_table1) <> max(in_table2);
In a real world case, you would probably have a separate table with all names. If so:
select n.*
from names n
where (not exists (select 1 from table1 t1 where t1.name = n.name) and
exists (select 1 from table2 t2 where t2.name = n.name
) or
(exists (select 1 from table1 t1 where t1.name = n.name) and
not exists (select 1 from table2 t2 where t2.name = n.name
);
This is usually the fastest approach because it does not involve any aggregation or duplicate removal.
If you want to use SET operator then find the solution as below:
CREATE TABLE TABLE1(NAME VARCHAR2(100));
CREATE TABLE TABLE2(NAME VARCHAR2(100));
INSERT INTO TABLE1 VALUES('A');
INSERT INTO TABLE1 VALUES('B');
INSERT INTO TABLE1 VALUES('C');
INSERT INTO TABLE2 VALUES('A');
INSERT INTO TABLE2 VALUES('B');
INSERT INTO TABLE2 VALUES('D');
SELECT
NAME
FROM
(
SELECT
NAME
FROM
TABLE1
UNION
SELECT
NAME
FROM
TABLE2
)
WHERE
NAME NOT IN (
SELECT
NAME
FROM
TABLE1
JOIN TABLE2 USING ( NAME )
);
Cheers!!
Yet another possible solution:
Find those that are in the first table but not in the second table using the MINUS operator (which is Oracle's implementation of the standard EXCEPT). Then UNION that with those that are in the second but not in the first.
(
select name
from t1
minus
select name
from t2
)
union all
(
select name
from t2
minus
select name
from t1
);
Given this setup:
create table t1
(
name varchar(10)
);
insert into t1 values ('Arthur');
insert into t1 values ('Zaphod');
create table t2
(
name varchar(10)
);
insert into t2 values ('Tricia');
insert into t2 values ('Zaphod');
This returns:
NAME
------
Arthur
Tricia
select id from((select id from table1)
union all
(select id from table2)) as t1
group by id having count(id)=1
Using the basic set operations the following query should work.
(
select name from table1
union all
select name from table2
)
minus
(
select name from table1
intersect
select name from table2
)
;
Regards
Akash

Conditional condition when joining tables in SQL Server

I have two tables both containing employee data, TableA and TableB, I'm joining them based on 2 id's (one it's userID and the other mID (month based id)) using a LEFT OUTER JOIN which returns NULL in about 20% of the results because TableB it's incomplete. I want -if possible- a query that detects if the join doesn't find a match and subtract one month to mID so the JOIN can cover at least a percentage of missing data with just old data.
I don't know if it's a way too complex query but I had in mind something like:
SELECT T1.*, T2.*
FROM TABLEA
LEFT OUTER JOIN TABLEB
ON T2.USERID = T1.USERID AND (CASE WHEN (T2.HID = T1.HID) = NULL THEN (T2.HID = T1.HID-1))
Appreciate any help.
I think this is what you are really wanting to do; the downside is there is a bit more logic and it would be duplicated per column, but it provides a fine grain of control.
DECLARE #TableA TABLE (USERID INT,HID INT,SomeData VARCHAR(20))
DECLARE #TableB TABLE (USERID INT,HID INT,SomeData VARCHAR(20))
INSERT INTO #TableA(USERID,HID,SomeData) SELECT 1,5,'Now'
INSERT INTO #TableA(USERID,HID,SomeData) SELECT 2,5,NULL
INSERT INTO #TableA(USERID,HID,SomeData) SELECT 3,5,NULL
INSERT INTO #Tableb(USERID,HID,SomeData) SELECT 2,4,'Now-1'
INSERT INTO #Tableb(USERID,HID,SomeData) SELECT 2,3,'Now-2'
INSERT INTO #Tableb(USERID,HID,SomeData) SELECT 3,4,'Now-1'
SELECT
t1.USERID, T1.Hid AS [Current HID]
,
CASE
WHEN T1.SomeData IS NOT NULL THEN T1.SomeData
WHEN T2.USERID IS NOT NULL THEN T2.SomeData
WHEN T3.USERID IS NOT NULL THEN T3.SomeData
ELSE T1.SomeData
END AS [Most Recent SomeData]
FROM #TABLEA T1
LEFT JOIN #TABLEB T2 ON T2.USERID = T1.USERID AND T2.HID = T1.HID
LEFT JOIN #TABLEB T3 ON T3.USERID = T1.USERID AND T3.HID = T1.HID-1
You were on the right track by using a case statement but it was just a little out of order. Warning! I didn't test it but I believe I ran into something similar in the past.
SELECT T1.*
,T2.*
FROM TABLEA t1
LEFT JOIN TABLEB t2 ON T2.USERID = T1.USERID
AND T2.HID = CASE
WHEN T2.HID = T1.HID then t1.hid
else T1.HID - 1
end

How to combine two different tables with different columns

I have a situation where I need to combine two different tables with different columns.
DDL:
create table tableA ([timestamp] datetime, [source] char(1), [description] varchar(20));
insert into tableA values
('2018-10-12', 'a', 'first day'),
('2018-10-13', 'b', 'alarms'),
('2018-10-14', 'c', 'processing');
create table tableB ([timestamp] datetime, entity varchar(20));
insert into tableB values
('2018-10-12', 'new env'),
('2018-10-13', 'resource'),
('2018-10-18', 'integrated');
I have different columns in two different tables. And I need to combine it as shown in screenshot using SQL.
Use union all
select a.timestamp, a.source,a.description,b.entity
from tableA a left join tableB b on a.timestamp=b.timestamp
where b.timestamp is not null
union all
select b.timestamp, a.source,a.description,b.entity
from tableA a right join tableB b on a.timestamp=b.timestamp
where a.timestamp is null
You can use INNER JOIN for this
SELECT a.TimeStamp, a.Source, a.Description, b.Entity
FROM TableA a
LEFT JOIN Tableb b ON a.TimeStamp=b.TimeStamp;
UNION
SELECT a.TimeStamp, a.Source, a.Description, b.Entity
FROM TableA a
RIGHT JOIN Tableb b ON a.TimeStamp=b.TimeStamp;
You need to use full join. Try this query:
select coalesce (a.timestamp, b.timestamp), source, description, entity
from tableA a
full join tableB b on a.timestamp = b.timestamp
Demo
Use Below code
SELECT isnull(t1.TimeStamp, t2.TimeStamp) TimeStamp, t1.source,t1.description, t2.entity from table1 t1 FULL OUTER JOIN table2 t2 on t1.id=t2.id

Get all records from Table When no records in Mapping Table

I have two tables in join. Here i have to get mapped records when the Second table has set of records. But when the Second table has no record i need all records from first table.
Create Table #temp1
(Id1 int)
Create Table #temp2
(Id2 int)
Insert into #temp1 Values(1),(2),(3),(4)
Insert into #temp2 Values(1),(2)
Select * from #temp1 A
Inner Join #temp2 B On A.Id1=B.Id2
It gives correct output when i have records in second table.
But when i have no records in second table i need to get all records from first table.
Delete #temp2
Select * from #temp1 A
Inner Join #temp2 B On A.Id1=B.Id2
this query return no records i tried with Left Outer Join it give all records but i am not getting records like first scenario.
Drop Table #temp1
Drop Table #temp2
I need output like this.
Thanks in Advance.
if question is not clear ,please ask me.
If you only want columns from the first table:
select a.*
from #temp1 a
where exists (select 1 from #temp2 b where b.id2 = a.id1) or
not exists (select 1 from #temp2 b);
If you wanted the extra columns from the second table, you could use union all:
Select a.*, b.*
from #temp1 a Inner Join
#temp2 b
On a.Id1 = b.Id2
union all
select a.*, b.*
from #temp1 a left join
#temp2 b
on a.id1 = b.id2
where not exists (select 1 from #temp2);
Actually you can use an OUTER JOIN:
SELECT Id1 FROM #temp1 t1
LEFT OUTER JOIN #temp2 t2
ON t1.Id1 = t2.Id2
Left join seems to work fine:
Select a.id1
from #temp1 A
left Join #temp2 B On A.Id1=B.Id2
Demo here

sql query without outer join key word

Is it possible to write a sql query where you know you have to use the left outer join..but cannot or are not allowed to use the "outer join" Key Word
I have two table sand want to get rows with null vaues from the left table ...this is pretty simple ...but am not supposed to use the key word....outer join....I need to right the logic for outer join myself
SELECT Field1
FROM table1
WHERE id NOT IN (SELECT id FROM table2)
SELECT Field1
FROM table1
WHERE NOT EXISTS (SELECT * FROM table2 where table2.id = table1.id)
This is something people do but it is deprecated and it does not currently work correctly (it sometimes will return a cross join instead of a left join) so it should NOT be used. I'm telling this only so you avoid using this solution.
SELECT Field1
FROM table1, table2 where table1.id *= table2.id
;WITH t1(c,d) AS
(
SELECT 1,'A' UNION ALL
SELECT 2,'B'
),t2(c,e) AS
(
SELECT 1,'C' UNION ALL
SELECT 1,'D' UNION ALL
SELECT 3,'E'
)
SELECT t1.c, t1.d, t2.c, t2.e
FROM t1, t2
WHERE t1.c = t2.c
UNION ALL
SELECT t1.c, t1.d, NULL, NULL
FROM t1
WHERE c NOT IN (SELECT c
FROM t2
WHERE c IS NOT NULL)
Returns
c d c e
----------- ---- ----------- ----
1 A 1 C
1 A 1 D
2 B NULL NULL
(Equivalent to)
SELECT t1.c, t1.d, t2.c, t2.e
FROM t1
LEFT JOIN t2
ON t1.c = t2.c
For SQL Server, you can just use LEFT JOIN - the OUTER is optional, just like INTO in an INSERT statement.
This is the same for all OUTER JOINs.
For an INNER JOIN you can just specify JOIN with no qualifiers and it is interpreted as an INNER JOIN.
This will give you all the rows in table A that don't have a matching row in table B:
SELECT *
FROM A
WHERE NOT EXISTS (
SELECT 1
FROM B
WHERE A.id = B.id
);
Returns all the matching rows from both tables:
SELECT a.*,b.* FROM table_a a, table_b b
WHERE a.key_field = b.key_field
Potential drawback is non-matches will be skipped.