How to write an SQL query for this scenerio? - sql-server

I'm trying to write a query where I want all the items in table1 that are in table2 which meets the first inner join criteria below (this is work find).
Then I want to check table3 to if there are exceptions. Exceptions are base on reference numbers (REF_NO). If the reference number exist in table3 then I need to check if the store number (STORE_NO) matches. If they match then I want the matching record from table1. If not then exclude the matching record from table1.
However, if there reference number DOES NOT exist in table3 then I want the record from table1 that match with table2.
Thanks
USE master
GO
table1
table2
table3
SELECT
T1.TERMINAL,
T1.OPERATOR,
T1.TRANS_NO,
T1.SEQ_NO,
T1.STORE_NO,
T2.REF_NO,
T2.SDATE,
T2.EDATE,
T1.POS_DATE,
T1.ITEM,
T1.ITYPE,
T1.SOLD_QTY,
T1.PRICE,
T2.OI_AMT
FROM [table1] As T1
INNER JOIN [table2] As T2
ON (T1.ITEM = T2.ITEM) And (T1.POS_DATE BETWEEN T2.SDATE And T2.EDATE)
INNER JOIN [table3] As T3
ON (T2.REF_NO = T3.REF_NO) And (T1.STORE_NO = T3.STORE)

SELECT
T1.TERMINAL,
T1.OPERATOR,
T1.TRANS_NO,
T1.SEQ_NO,
T1.STORE_NO,
T2.REF_NO,
T2.SDATE,
T2.EDATE,
T1.POS_DATE,
T1.ITEM,
T1.ITYPE,
T1.SOLD_QTY,
T1.PRICE,
T2.OI_AMT
FROM [table1] As T1
INNER JOIN [table2] As T2
ON T1.ITEM = T2.ITEM
AND T1.POS_DATE > T2.SDATE
AND T1.POS_DATE <= T2.EDATE
WHERE EXISTS ( SELECT TOP (1) 1
FROM table3 as T31
WHERE T2.REF_NO = T31.REF_NO
AND T1.STORE_NO = T31.STORE)
OR NOT EXISTS ( SELECT TOP (1) 1
FROM table3 as T32
WHERE T2.REF_NO = T32.REF_NO)
This should work, it checks both of your conditions. I also would encourage you not to use BETWEEN clause and specify date ranges using two conditions.

Related

MSSQL - Adding condition on the where and on clause performance

Consider the following two queries:
select *
from
table1 t1
left join
table2 t2
on t1.Id = t2.t1Id and (t1.Status = 1 or t2.Id is not null)
And this one
select *
from
table1 t1
left join
table2 t2
on t1.Id = t2.t1Id
where
t1.Status = 1 or t2.Id is not null
The first one runs in 2 seconds. The second one in 2 minutes. Shouldn't the execution plan be the same?
The query plans are different because the queries (and results) are different.
You're using a LEFT JOIN, so the first query will return rows with NULL values where not in table 2.
The second query will not return those rows.
If it was an INNER JOIN, they would essentially be the same query.
Here the Below Query Returns all the "Table1" results with additional matching Columns based on the "ON Clause" condition.
select * from table1 t1
left join table2 t2
on t1.Id = t2.t1Id and (t1.Status = 1 or t2.Id is not null)
Now, the below query matches the 2 tables and returns the rows based on the ON Clause and an additional WHERE Clause filters the Rows again based on the Condition.
select * from
table1 t1
left join table2 t2 on t1.Id = t2.t1Id
where t1.Status = 1 or t2.Id is not null
Here, Even though we used LEFT JOIN But in this case it acts like an INNER JOIN
So, Here Both the Queries produce Different Result Sets. The Execution Plan Also Vary which results in Different Execution Time.
The best way to deal with an OR is to eliminate it (if possible) or break it into smaller queries. Breaking a short and simple query into a longer, more drawn-out query may not seem elegant, but when dealing with OR problems, it is often the best choice:
select *
from table1 t1
left join table2 t2 t1.Id = t2.t1Id
where t1.Status = 1
union all
select *
from table1 t1
left join table2 t2 t1.Id = t2.t1Id
where t2.Id is not null
You can read more in this article:
https://www.sqlshack.com/query-optimization-techniques-in-sql-server-tips-and-tricks/

Using inner join to reduce results. Do I need to reference new table anywhere beyond the join statement?

I'm wondering if I can just do an inner join as kind of a where clause by itself. Or if I use a field from the joined table in my where clause, if it's redundant.
select * from T1 inner join T2 on T1.id = T2.id where T2.z is not null
Is the "T2.z is null" part redundant if all I want returned are records in T1 where the same id exists in T2?
For one thing, select * from t1 inner join t2 [...] will not return records in t1 - it will return all the columns of t1 and t2. You could fix that by selecting specifically the columns in t1 - don't select *.
Then, if there are many rows in t2 with the same t2.id, matching a given t1.id, you will get a whole bunch of rows in the result for that one row in the input t1. So you will not always "reduce" the result set.
It seems what you want can be achieved with the in operator, something like
select * from t1 where t1.id in (select id from t2);
This is equivalent to the following modification of your query. You do not need a where clause for this to work:
select t1.* from t1 inner join (select distinct id from t2) b on t1.id = b.id;
In the following query,
select t1.* from T1 inner join T2 on T1.id = T2.id where T2.z is NOT null
The WHERE condition is redundant, assuming that T2.Z is a NOT NULL column.
That would leave you with this:
select t1.* from T1 inner join T2 on T1.id = T2.id
, which is a little odd because, in a normally designed database, either T1.id or T2.id would be the primary key of its table.
If T1.id is the primary key of T1, then your query is going to return duplicates -- each T1 row will be repeated once for each child that exists in T2.
If T2.id is the primary key of T2, then you should not need to join to T2 at all, because every possible T1.id value must exist in T2.id, because of the FOREIGN KEY relationship that (should) exist. In that case, you could have written:
select t1.* from T1 WHERE T1.id is not null;
So, the answer to your question is that you do not need to reference the tables outside of the join condition in order for the join to be applied. But something seems a little off about the approach.

SQL Join Multiple columns in one table with one column in another table

I got two tables as below
table 1 :Which got multiple columns which refers to a value in the second table.
tbale2:Lookup table where it got a row for every possible value for the columns in the above table
What I want to do is lookup the values in columns ItemID,ORDIG,CatID,MediaID in table 1 from ValueID in table2 and return ValueName
so at the end my result should look like
ItemID OrgID CatID MediaID
i859049 Singapore Full 0001edf
You will need to join to the lookup table once for each value you need, but should likely need to use a LEFT instead of INNER join since the values may be null.
SELECT
T1.ItemId,
Items.ValueName AS ItemName,
T1.OrgID,
Orgs.ValueName AS OrgName,
T1.CatID,
Cats.ValueName AS CatName,
T1.MediaID,
Media.ValueName AS MediaName
FROM Table1 T1
LEFT OUTER JOIN Table2 Items
ON T1.ItemId = Items.ValueID
LEFT OUTER JOIN Table2 Orgs
ON T1.OrgId = Orgs.ValueID
LEFT OUTER JOIN Table2 Cats
ON T1.CatId = Cats.ValueID
LEFT OUTER JOIN Table2 Media
ON T1.MediaId = Media.ValueID

Not in Not exists

I have three tables
table1 -> xt1, yt1, zt1;
table2 -> xt2
table3 -> yt3, zt3
SELECT xt1, yt1, zt1
From table1, table3
Where xt1
NOT IN
(SELECT DISTINCT table1.xt1 FROM table2 INNER JOIN table1 ON
table1.xt1 = Replace(table2.xt2,',',''))
And table1.yt1 = table3.yt3
AND table1.zt1 = table3.zt3
it is working correctly but i take long time.
if i replace NOT IN with Not exists it return empty set.
SELECT xt1, yt1, zt1
From table1, table3
Where Not exists
(SELECT DISTINCT table1.xt1 FROM table2 INNER JOIN table1 ON
table1.xt1 = Replace(table2.xt2,',',''))
And table1.yt1 = table3.yt3
AND table1.zt1 = table3.zt3
the results of the second select should be 6 rows but it returns notiong with not exists.
also if i tried to change the compare part to
table1.xt1 != Replace(table2.xt2,',','') and remove the NOT IN
select it get outof memory error.
So is this the best way to write my query and why it return empty set with Not exists
thank you.
Ok, first of all, I changed your implicit join to an explicit one. Then I fixed the NOT EXISTS so it correlates to the outer table1:
SELECT t1.xt1, t1.yt1, t1.zt1
FROM table1 AS t1
INNER JOIN table3 AS t3
ON t1.yt1 = t3.yt3
AND t1.zt1 = t3.zt3
WHERE NOT EXISTS ( SELECT 1
FROM table2 AS t2
INNER JOIN table1 AS t1_1
ON t1_1.xt1 = REPLACE(t2.xt2,',','')
AND t1_1.xt1 = t1.xt1) ;
which can be simplified further to:
SELECT t1.xt1, t1.yt1, t1.zt1
FROM table1 AS t1
INNER JOIN table3 AS t3
ON t1.yt1 = t3.yt3
AND t1.zt1 = t3.zt3
WHERE NOT EXISTS ( SELECT 1
FROM table2 AS t2
WHERE t1.xt1 = REPLACE(t2.xt2,',','')
) ;
You need to select IN or exist depending upon the size of inner query. when there is a outer query and inner-sub-query, if the result of sub query is small, In is preferred as outer query is selected based upon result of sub-query.
if the result of sub-query is large, exist is preferred as outer query is evaluated first.

Optimize CASE Test in SQL Server

I'm wondering if there's any way to optimize the following SELECT query. (Note: I typed this when writing my question for nonexistent tables and I might not have the correct syntax.)
The goal is, if Table2 contains any related rows I want to set the value of the third column to the number of related rows in Table2. Otherwise, if Table3 contains any related rows I want to set the column to the number of related rows in Table3. Otherwise, I want to set the column value to 0.
SELECT Id, Title,
CASE
WHEN EXISTS (SELECT * FROM Table2 t2 WHERE t2.RelatedId = Table1.Id) THEN
(SELECT COUNT(1) FROM Table2 t2 WHERE t2.RelatedId = Table1.Id)
WHEN EXISTS (SELECT * FROM Table3 t3 WHERE t3.RelatedId = Table1.Id) THEN
(SELECT COUNT(1) FROM Table3 t3 WHERE t3.RelatedId = Table1.Id)
ELSE 0
END AS RelatedCount
FROM Table1
I don't like the fact that I'm basically performing the same query twice (in two cases). Is there any way to do what I want while only performing the query once?
Note that this is part of a much larger query with multiple JOINs and UNIONs so it's not easy to take a completely different approach.
This query should perform much better. You are not just performing the same query twice; since they are correlated subqueries, they will run once per row.
SELECT Id, Title,
coalesce(t2.Count, t3.Count, 0) AS RelatedCount
FROM Table1 t
left outer join (
SELECT RelatedId, count(*) as Count
FROM Table2
group by RelatedId
) t2 on t1.Id = t2.RelatedId
left outer join (
SELECT RelatedId, count(*) as Count
FROM Table3
group by RelatedId
) t3 on t1.Id = t3.RelatedId

Resources