Slow query when used as EXISTS subquery - tsql

I have the following query:
SELECT I.InsuranceID
FROM Insurance I
INNER JOIN JobDetail JD ON I.AccountID = JD.AccountID
WHERE I.InsuranceLookupID IS NULL
AND JD.JobID = 28
It executes in about a second. When used as a subquery as follows:
IF EXISTS(
SELECT I.InsuranceID
FROM Insurance I
INNER JOIN JobDetail JD ON I.AccountID = JD.AccountID
WHERE I.InsuranceLookupID IS NULL
AND JD.JobID = 28
)
SELECT 1
ELSE
SELECT 0
It takes 90 seconds. It's my understanding that EXISTS is supposed to be optimized to stop after finding the first record. Why would this take longer?

I've seen this myself.
I can guess that EXISTS is better in a WHERE clause because it gives a semi-join which is set based, And exactly what you need.
In an IF, this isn't clear to the optimiser. That is, there is nothing to semi-join too. This should hopefully be the same (bad that is):
SELECT 1 WHERE EXISTS (SELECT I.InsuranceID
FROM Insurance I
INNER JOIN JobDetail JD ON I.AccountID = JD.AccountID
WHERE I.InsuranceLookupID IS NULL
AND JD.JobID = 28)
You could to this though
SELECT SIGN(COUNT(*))
FROM Insurance I
INNER JOIN JobDetail JD ON I.AccountID = JD.AccountID
WHERE I.InsuranceLookupID IS NULL
AND JD.JobID = 28
It is optimised in some circumstances:
What's the best to check if item exist or not: Select Count(ID)OR Exist(...)?
Not sure what confuses the optimiser...

Related

MYSQL- query too slow to load

My query is working but it takes time to display the data. Can you help me to make it quick.
$sql="SELECT allinvty3.*, stock_transfer_tb.* from stock_transfer_tb
INNER JOIN allinvty3 on stock_transfer_tb.in_code = allinvty3.in_code
where stock_transfer_tb.in_code NOT IN (SELECT barcode.itemcode from barcode where stock_transfer_tb.refnumber = barcode.refitem)";
I would recommend using the following query:
SELECT
a.*,
s.*
FROM stock_transfer_tb s
INNER JOIN allinvty3 a
ON s.in_code = a.in_code
WHERE
NOT EXISTS (SELECT 1 FROM barcode b
WHERE s.refnumber = b.refitem AND s.in_code = b.itemcode);
If this still doesn't give you the performance you want, then you should look into adding indices on all columns involved in the join and where clause.

TSQL -- Where Statements on Multiple columns in Update

My basic question has to do with updating multiple columns at once from specified values in my query. The reason I want to do this is that I am updating my values from a ginormous table so I only want to query it once in order to reduce run time. Here is an example of an example select statement that returns the value I want for just one of the columns I need to update:
select a.Value
from Table1
left outer join
(
select ID, FilterCol1, FilterCol2, Value
from Table2
) a on a.ID = Table1.ID
where {Condition1a on FilterCol1}
and {Condition2a on FilterCol2}
In order to update multiple columns at once I would like to be able do something like this (but it returns NULL):
Update T1
set T1Value1 = (select a.Value where {Condition1a on FilterCol1}
and {Condition2a on FilterCol2)
,T1Value2 = (select a.Value where {Condition1b on FilterCol1}
and {Condition2b on FilterCol2})
from Table1 T1
left outer join
(
select ID, FilterCol1, FilterCol2, Value
from Table2
) a on a.ID = Table1.ID
Any help figuring this out would be greatly appreciated, let me know if you have any questions or if I made any errors. Thanks!
EDIT: I think I have identified the problem, but I'm not sure of a solution yet. I think seeing the issue requires a little more context: The select from table 2 is actually an unpivot on a wide table. This means that when the left outer join is applied, there will be multiple rows for a given ID. What the case statement that Earl suggested seems to be doing (and I assume this is happening with the where clause as well) is comparing my Conditions to only the first row of the columns from a. Since my conditions are meant to help determine which of the rows from a is chosen, they will always evaluate false for the first row (I know this just from what I know about the data), hence my perpetual NULL values. Does anyone know of a workaround to look at the other rows in a?
UPDATE T1
SET T1Value1 = CASE WHEN (FilterCol1 = Condition1a AND FilterCol2 = Condition2a) THEN a.Value END,
T1Value2 = CASE WHEN (FilterCol1 = Condition1b AND FilterCol2 = Condition2b) THEN a.Value END
FROM Table1 T1
left outer join
(
select ID, FilterCol1, FilterCol2, Value
) a on a.ID = Table1.ID

Postgres join not respecting outer where clause

In SQL Server, I know for sure that the following query;
SELECT things.*
FROM things
LEFT OUTER JOIN (
SELECT thingreadings.thingid, reading
FROM thingreadings
INNER JOIN things on thingreadings.thingid = things.id
ORDER BY reading DESC LIMIT 1) AS readings
ON things.id = readings.thingid
WHERE things.id = '1'
Would join against thingreadings only once the WHERE id = 1 had restricted the record set down. It left joins against just one row. However in order for performance to be acceptable in postgres, I have to add the WHERE id= 1 to the INNER JOIN things on thingreadings.thingid = things.id line too.
This isn't ideal; is it possible to force postgres to know that what I am joining against is only one row without explicitly adding the WHERE clauses everywhere?
An example of this problem can be seen here;
I am trying to recreate the following query in a more efficient way;
SELECT things.id, things.name,
(SELECT thingreadings.id FROM thingreadings WHERE thingid = things.id ORDER BY id DESC LIMIT 1),
(SELECT thingreadings.reading FROM thingreadings WHERE thingid = things.id ORDER BY id DESC LIMIT 1)
FROM things
WHERE id IN (1,2)
http://sqlfiddle.com/#!15/a172c/2
Not really sure why you did all that work. Isn't the inner query enough?
SELECT t.*
FROM thingreadings tr
INNER JOIN things t on tr.thingid = t.id AND t.id = '1'
ORDER BY tr.reading DESC
LIMIT 1;
sqlfiddle demo
When you want to select the latest value for each thingID, you can do:
SELECT t.*,a.reading
FROM things t
INNER JOIN (
SELECT t1.*
FROM thingreadings t1
LEFT JOIN thingreadings t2
ON (t1.thingid = t2.thingid AND t1.reading < t2.reading)
WHERE t2.thingid IS NULL
) a ON a.thingid = t.id
sqlfiddle demo
The derived table gets you the record with the most recent reading, then the JOIN gets you the information from things table for that record.
The where clause in SQL applies to the result set you're requesting, NOT to the join.
What your code is NOT saying: "do this join only for the ID of 1"...
What your code IS saying: "do this join, then pull records out of it where the ID is 1"...
This is why you need the inner where clause. Incidentally, I also think Filipe is right about the unnecessary code.

T-SQL query one table, get presence or absence of other table value

I'm not sure what this type of query is called so I've been unable to search for it properly. I've got two tables, Table A has about 10,000 rows. Table B has a variable amount of rows.
I want to write a query that gets all of Table A's results but with an added column, the value of that column is a boolean that says whether the result also appears in Table B.
I've written this query which works but is slow, it doesn't use a boolean but rather a count that will be either zero or one. Any suggested improvements are gratefully accepted:
SELECT u.number,u.name,u.deliveryaddress,
(SELECT COUNT(productUserid)
FROM ProductUser
WHERE number = u.number and productid = #ProductId)
AS IsInPromo
FROM Users u
UPDATE
I've run the query with actual execution plan enabled, I'm not sure how to show the results but various costs are:
Nested Loops (left semi join): 29%]
Clustered Index scan (User Table): 41%
Clustered Index Scan (ProductUser table): 29%
NUMBERS
There are 7366 users in the users table and currently 18 rows in the productUser table (although this will change and could be in the thousands)
You can use EXISTS to short circuit after the first row is found rather than COUNT-ing all matching rows.
SQL Server does not have a boolean datatype. The closest equivalent is BIT
SELECT u.number,
u.name,
u.deliveryaddress,
CASE
WHEN EXISTS (SELECT *
FROM ProductUser
WHERE number = u.number
AND productid = #ProductId) THEN CAST(1 AS BIT)
ELSE CAST(0 AS BIT)
END AS IsInPromo
FROM Users u
RE: "I'm not sure what this type of query is called". This will give a plan with a semi join. See Subqueries in CASE Expressions for more about this.
Which management system are you using?
Try this:
SELECT u.number,u.name,u.deliveryaddress,
case when COUNT(p.productUserid) > 0 then 1 else 0 end
FROM Users u
left join ProductUser p on p.number = u.number and productid = #ProductId
group by u.number,u.name,u.deliveryaddress
UPD: this could be faster using mssql
;with fff as
(
select distinct p.number from ProductUser p where p.productid = #ProductId
)
select u.number,u.name,u.deliveryaddress,
case when isnull(f.number, 0) = 0 then 0 else 1 end
from Users u left join fff f on f.number = u.number
Since you seem concerned about performance, this query can perform faster as this will cause index seek on both tables versus an index scan:
SELECT u.number,
u.name,
u.deliveryaddress,
ISNULL(p.number, 0) IsInPromo
FROM Users u
LEFT JOIN ProductUser p ON p.number = u.number
WHERE p.productid = #ProductId

T-SQL Problem converting a Cursor into a SET based operation

Basically I have this cursor that was not written by me but is taking some time to process and I was wanting to try and improve it by getting rid of the cursor all together.
Here is the code:
DECLARE #class_id int, #title_code varchar(30)
DECLARE title_class CURSOR FOR
SELECT DISTINCT title_code FROM tmp_business_class_titles (NOLOCK)
OPEN title_class
FETCH title_class INTO #title_code
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT TOP 1 #class_id = bc1.categoryid
FROM tmp_business_class_titles bct,
dbo.Categories bc1 (nolock)
join dbo.Categories bc2 (nolock) on bc2.categoryid = bc1.highercategoryid
join dbo.Categories bc3 (nolock) on bc3.categoryid = bc2.highercategoryid
WHERE bc1.categoryid = bct.class_id
AND title_code = #title_code
ORDER BY Default_Flag DESC
UPDATE products
SET subcategoryid = #class_id
WHERE ccode = #title_code
AND spdisplaytype = 'Table'
UPDATE products
SET subcategoryid = #class_id
WHERE highercatalogid IN (
SELECT catalogid FROM products (nolock)
WHERE ccode = #title_code AND spdisplaytype = 'Table')
FETCH title_class INTO #title_code
END
CLOSE title_class
DEALLOCATE title_class
The table tmp_business_class_titles looks like this:
class_id,title_code,Default_flag
7,101WGA,0
7,10315,0
29,8600,0
The default flag can always be 0 but if it is 1 then the logic should automatically pick the default class_id for that title_id.
So the current logic loops through the above table in a cursor and then selects the top 1 class id for each title, ordered by the the default flag (so the class_id with a default_flag of 1 should always be returned first.) and applies the default class_id to the products table.
This code takes around 1:20 to run and I am trying to convert this into one or 2 update statements but I have exhausted my brain in doing so.
Any TSQL Guru's have any ideas if this is possible or should I re-evaluate the entire logic on how the default flag works?
cheers for any help.
I don't have quite enough information to work with, so the following query is likely to fail. I particularly need more information on the products table to make this work, but assuming that you have SQL Server 2005 or higher, this might be enough to get you started in the right direction. It utilizes common table expressions along with the RANK function. I highly recommend learning about them, and in all likelihood, it will greatly improve the efficiency of the query.
;WITH cteTitle As (
SELECT
sequence = RANK() OVER (PARTITION BY bct.title_code ORDER BY Default_Flag desc)
,bct.title_code
,bc1.categoryid
FROM
tmp_business_class_titles bct
join Categories bc1 ON bc1.categoryid = bct.class_id
join Categories bc2 ON bc2.categoryid = bc1.highercategoryid
join Categories bc3 ON bc3.categoryid = bc2.highercategoryid
)
UPDATE
prod
SET
subcategoryid = ISNULL(t.categoryid,t2.categoryid)
FROM
products prod
LEFT join products subprod ON subprod.catalogid = prod.highercatalogid
LEFT join cteTitle t ON prod.ccode = t.title_code AND t.sequence = 1 AND prod.spdisplaytype = 'Table'
LEFT join cteTitle t2 ON subprod.ccode = t2.title_code And t2.sequence = 1 AND subprod.spdisplaytype = 'Table'
WHERE
t2.categoryid IS NOT NULL