I have a table contains 100 million rows and I found the below query (A), which is taking time. I am thinking to change it to (B), is it the same?
Is this query (A) :
SELECT *
FROM tab1
WHERE code = 1
AND TYPE = 'A'
AND 0 = (SELECT Count(1)
FROM tab1
WHERE code = 1
AND tr_type = 'APPROVE'
AND security = 'Y')
Similar to this (B) ?
SELECT *
FROM tab1
WHERE code = 1
AND TYPE = 'A'
AND NOT EXISTS (SELECT 1
FROM tab1
WHERE code = 1
AND tr_type = 'APPROVE'
AND security = 'Y'
AND ROWNUM = 1)
I would recommend writing the query as:
SELECT t1.*
FROM tab1 t1
WHERE t1.code = 1 AND
t1.TYPE = 'A' AND
NOT EXISTS (SELECT 1
FROM tab1 tt1
WHERE tt1.code = t1.code AND
tt1.tr_type = 'APPROVE'
tt1.security = 'Y'
);
The changes are:
Table aliases are introduced.
All columns are qualified.
The rownum = 1 condition is removed.
The latter is between redundant and dangerous. EXISTS/NOT EXISTS is already looking for any row that matches. Including rownum could affect the optimizer.
Your first version has to find all matches in order to calculate the count. That can be much more expensive that determining that there are no matches or finding the first one.
For performance, you want indexes on tab1(code, tr_type, security) and tab1(code, type).
Yes same query.
SELECT *
FROM tab1
WHERE code = 1
AND type = 'A'
AND 0 = (SELECT Count(1)
FROM tab1
WHERE code = 1
AND tr_type = 'APPROVE'
AND security = 'Y')
If we will take output of inner query in the above query
SELECT Count(1)
FROM tab1
WHERE code = 1
AND tr_type = 'APPROVE'
AND security = 'Y'
I am assuming , Record with code = 1 , TR_TYPE = 'APPROVE' and security = 'Y' is present. So output will be 1. (Assumption is only one matching record is present).
SELECT *
FROM tab1
WHERE code =1
AND type='A'
AND 0=1 (replacing the result)
So this will not return any data as 0 is never equal to 1.
SELECT *
FROM tab1
WHERE code = 1
AND type = 'A'
AND NOT EXISTS (SELECT 1
FROM tab1
WHERE code = 1
AND tr_type = 'APPROVE'
AND security = 'Y'
AND rownum = 1)
Now checking the above query, inner query return 1 as the data with the where clause is present so result will be
SELECT *
FROM tab1
WHERE code =1
AND type='A'
AND NOT EXISTS (1)
Exists(If any record found) = true and in our case Exists(1) = true so
Not Exist (1) = false. Which denotes to false as it has 1 record , so it will also not return the data.
So if you are asking that both query will return same output. So It will return same output.
Yes the output will be same because its just the interchangeable way to use it however in terms of time complexity the second one is optimised.
Yes they are the same, however i would explicitly put an alias on the table being used in the non exists and alias the columns being compared to.Eg:
SELECT *
FROM tab1
WHERE code = 1
AND type = 'A'
AND NOT EXISTS (SELECT 1
FROM tab1 b
WHERE b.code = 1
AND b.tr_type = 'APPROVE'
AND b.security = 'Y'
AND rownum = 1)
Related
SELECT 1 FROM
(SELECT 1 FROM mytable1 WHERE parentid = 'ID1' AND flag = 'Y') as X,
(SELECT 1 FROM mytable2 WHERE id = 'ID2' AND flag = 'Y') as Y
I'm making a query to see if two flags are set in two tables, where 'parentid' and 'id' are both primary keys. The query should return a row only if both flags are set to 'Y', or return nothing otherwise, then I do stuff with that result in my backend code.
I've tested this and it works but I feel like it looks wonky and could be optimized. Any ideas?
To get what You want:
SELECT 1
FROM mytable1 AS a, mytable2 AS b
WHERE a.parentid = 'ID1' AND a.flag = 'Y'
AND b.id = 'ID2' AND b.flag = 'Y'
But in fact, I would prefer a query with LEFT JOIN, which always gives one row, like this:
SELECT CASE WHEN a.flag = 'Y' AND b.flag = 'Y' THEN 1 ELSE 0 END AS result
FROM TABLE ( VALUES 1 ) AS always(present)
LEFT JOIN mytable1 AS a ON a.parentid = 'ID1'
LEFT JOIN mytable2 AS b ON b.id = 'ID2'
Your query is fine (although I would use CROSS JOIN. However, I would prefer a row with a specific value. I would phrase that as:
SELECT (CASE WHEN EXISTS (SELECT 1 FROM mytable1 WHERE parentid = 'ID1' AND flag = 'Y') AND
EXISTS (SELECT 1 FROM mytable2 WHERE id = 'ID2' AND flag = 'Y')
THEN 1 ELSE 0
END) as flag
You may need from dual, depending on your database.
It better to use JOIN instead of doing subqueries
SELECT mytable1.parentid, mytable2.id
FROM mytable1
JOIN mytable2 ON mytable2.flag = "Y" AND mytable1.flag = "Y"
How can I check and store in variable whether two tables have identical content?
I have table variable with data like
declare #table1 table (id int)
insert into #table1 (id) values (1), (2), (3)
and as the second table I have query
select T.id from SomeTable T
inner join #table1 T1 on T.id = T1.id
the query returns data:
id
-----
1
2
In this case I need write false(0) into declare #HasAccess BIT variable.
When the query returns data:
id
-----
1
2
3
then I need write true(1) into #HasAccess
Hmmm. There are various ways.
Given that you have one column, you can do:
select (case when count(*) = 0 then 1 else 0 end)
from t1 full join
t2
on t1.id = t2.id
where t1.id is null or t2.id is null;
This checks if an id doesn't match in either table.
Another way uses union all:
select (case when count(*) = 0 then 1 else 0 end)
from (select id, sum(in_t1) as num_t1, sum(in_t2) as num_t2)
from ((select id, 1 as in_t1, 0 as in_t2 from table1) union all
(select id, 0, 1 from table2)
) tt
group by id
) tt
where num_t1 <> 1 or num_t2 <> 1;
Another option (just for fun). This will compare the entire table fields and values.
I suspect not the best option for LARGE tables
Example
Select IsIdentical = case when (Select * from Table1 Order by id For XML Raw)
= (Select * from Table2 Order by id For XML Raw)
then 1 else 0 end
EDIT - Option with Inner Join
Select IsIdentical = case when (Select * from #Table1 Order by id For XML Raw)
= (Select A.*
From SomeTable A
Join #Table1 B on A.ID=B.ID
Order By id For XML Raw)
then 1 else 0 end
Using EXCEPT:
SET #HasAccess = ISNULL(
( SELECT 0
WHERE EXISTS(
SELECT ID /* add more columns here if needed */
FROM #table1
EXCEPT
SELECT ID /* add more columns here if needed */
FROM SomeTable )), 1 )
Explanation:
Return all IDs from #table1, except those found in SomeTable
Return 0 (false) if any records have been returned by [1].
If no records returned by [1] the main query will return NULL, hence the ISNULL
Advanatages
Can easily be extended to comparisons on more than one column.
I am asking you how can I mimic this logic in SQL:
SELECT var1, var2 FROM table1,WHERE var1 = COUNT(table1.status == 1) AND var2 = COUNT(table2.status == 2)
I want to store in var1 the number of entries that have status = 1 and in var2 the number of entries which have status = 2 in a single SELECT.
You can use COUNT combined with CASE:
SELECT
COUNT(CASE WHEN t1.Status = 1 THEN 1 END) AS var1,
COUNT(CASE WHEN t1.Status = 2 THEN 1 END) AS var2
FROM table1 t1;
Alternatively you can use SUM:
SELECT
SUM(t1.status = 1) AS var1,
SUM(t1.status = 2) AS var2
FROM table1 t1;
If your table is large and has an index on the status column, you need to be able to use an index to get fast exection (two index lookups are faster than one table scan), and that requires to filter the rows with WHERE.
If you can handle the result as two rows, use a compound query:
SELECT count(*) FROM Table1 WHERE status = 1
UNION ALL
SELECT count(*) FROM Table1 WHERE status = 2;
If you need the results in two columns, you can use subqueries:
SELECT (SELECT count(*) FROM Table1 WHERE status = 1) AS var1,
(SELECT count(*) FROM Table1 WHERE status = 2) AS var2;
If you want set two result difference in two variables in a one row this is the option.
SELECT
#var1 = t1.column,
#var2 = t2.column
FROM table1 t1 INNER JOIN table1 t2 ON
a.column = 'value' AND
b.column = 'value2';
Use subquery,Fetch count using count() method of sql
So,
SELECT var1, var2 FROM table1 WHERE var1 = (SELECT count(*) from table 1 where table1.status == 1) AND var2 = (SELECT count(*) from table 1 where table1.status == 2)
SELECT *
FROM FirstTable
WHERE RowProcessed = 'N'
AND (
CASE
WHEN EXISTS(SELECT top 1 FROM SecondTable)
THEN 1
ELSE EXISTS(
SELECT SecondTable.RowProcessed
FROM SecondTable
WHERE FirstTable.Key = SecondTable.Key
AND SecondTable.RowProcessed = 'Y'
)
END
)
AND OtherConditions
Case When then else in where clause. Not sure about the syntax.
I would like to verify there are rows in the SecondTable and if there are rows check for another condition, that is basicallly what I want to do.
Perhaps this is what you want? If there are rows in SecondTable, then do the second EXISTS:
SELECT * FROM FirstTable
WHERE RowProcessed = 'N'
AND (NOT EXISTS (SELECT 1 from SecondTable)
OR EXISTS (SELECT 1 FROM SecondTable
WHERE FirstTable.Key = SecondTable.Key
and SecondTable.RowProcessed = 'Y'))
AND OtherConditions
I have a table where one column has duplicate records but other columns are distinct. so something like this
Code SubCode version status
1234 D1 1 A
1234 D1 0 P
1234 DA 1 A
1234 DB 1 P
5678 BB 1 A
5678 BB 0 P
5678 BP 1 A
5678 BJ 1 A
0987 HH 1 A
So in the above table. subcode and Version are unique values whereas Code is repeated. I want to transfer records from the above table into a temporary table. Only records I would like to transfer are where ALL the subcodes for a code have status of 'A' and I want them in the temp table only once.
So from example above. the temporary table should only have
5678 and 0987 since all the subcodes relative to 5678 have status of 'A' and all subcodes for 0987 (it only has one) have status of A. 1234 is ommited because its subcode 'DB' has status of 'P'
I'd appreciate any help!
Here's my solution
SELECT Code
FROM
(
SELECT
Code,
COUNT(SubCode) as SubCodeCount
SUM(CASE WHEN ACount > 0 THEN 1 ELSE 0 END)
as SubCodeCountWithA
FROM
(
SELECT
Code,
SubCode,
SUM(CASE WHEN Status = 'A' THEN 1 ELSE 0 END)
as ACount
FROM CodeTable
GROUP BY Code, SubCode
) sub
GROUP BY Code
) sub2
WHERE SubCodeCountWithA = SubCodeCount
Let's break it down from the inside out.
SELECT
Code,
SubCode,
SUM(CASE WHEN Status = 'A' THEN 1 ELSE 0 END)
as ACount
FROM CodeTable
GROUP BY Code, SubCode
Group up the codes and subcodes (Each row is a distinct pairing of Code and Subcode). See how many A's occured in each pairing.
SELECT
Code,
COUNT(SubCode) as SubCodeCount
SUM(CASE WHEN ACount > 0 THEN 1 ELSE 0 END)
as SubCodeCountWithA
FROM
--previous
GROUP BY Code
Regroup those pairings by Code (now each row is a Code) and count how many subcodes there are, and how many subcodes had an A.
SELECT Code
FROM
--previous
WHERE SubCodeCountWithA = SubCodeCount
Emit those codes with have the same number of subcodes as subcodes with A's.
It's a little unclear as to whether or not the version column comes into play. For example, do you only want to consider rows with the largest version or if ANY subcde has an "A" should it count. Take 5678, BB for example, where version 1 has an "A" and version 0 has a "B". Is 5678 included because at least one of subcode BB has an "A" or is it because version 1 has an "A".
The following code assumes that you want all codes where every subcode has at least one "A" regardless of the version.
SELECT
T1.code,
T1.subcode,
T1.version,
T1.status
FROM
MyTable T1
WHERE
(
SELECT COUNT(DISTINCT subcode)
FROM MyTable T2
WHERE T2.code = T1.code
) =
(
SELECT COUNT(DISTINCT subcode)
FROM MyTable T3
WHERE T3.code = T1.code AND T3.status = 'A'
)
Performance may be abysmal if your table is large. I'll try to come up with a query that is likely to have better performance since this was off the top of my head.
Also, if you explain the full extent of your problem maybe we can find a way to get rid of that temp table... ;)
Here are two more possible methods. Still a lot of subqueries, but they look like they will perform better than the method above. They are both very similar, although the second one here had a better query plan in my DB. Of course, with limited data and no indexing that's not a great test. You should try all of the methods out and see which is best for your database.
SELECT
T1.code,
T1.subcode,
T1.version,
T1.status
FROM
MyTable T1
WHERE
EXISTS
(
SELECT *
FROM MyTable T2
WHERE T2.code = T1.code
AND T2.status = 'A'
) AND
NOT EXISTS
(
SELECT *
FROM MyTable T3
LEFT OUTER JOIN MyTable T4 ON
T4.code = T3.code AND
T4.subcode = T3.subcode AND
T4.status = 'A'
WHERE T3.code = T1.code
AND T3.status <> 'A'
AND T4.code IS NULL
)
SELECT
T1.code,
T1.subcode,
T1.version,
T1.status
FROM
MyTable T1
WHERE
EXISTS
(
SELECT *
FROM MyTable T2
WHERE T2.code = T1.code
AND T2.status = 'A'
) AND
NOT EXISTS
(
SELECT *
FROM MyTable T3
WHERE T3.code = T1.code
AND T3.status <> 'A'
AND NOT EXISTS
(
SELECT *
FROM MyTable T4
WHERE T4.code = T3.code
AND T4.subcode = T3.subcode
AND T4.status = 'A'
)
)
In your select, add a where clause that reads:
Select [stuff]
From Table T
Where Exists
(Select * From Table
Where Code = T.Code
And Status = 'A')
And Not Exists
(Select * From Table I
Where Code = T.Code
And Not Exists
(Select * From Table
Where Code = I.Code
And SubCode = I.SubCode
And Status = 'A'))
In English,
Show me the rows,
where there is at least one row with status 'A',
and there are NO rows with any specific subcode,
that do not have at least one row with that code/subcode, with status 'A'
INSERT theTempTable (Code)
SELECT t.Code
FROM theTable t
LEFT OUTER JOIN theTable subT ON (t.Code = subT.Code AND subT.status <> 'A')
WHERE subT.Code IS NULL
GROUP BY t.Code
This should do the trick. The logic is a little tricky, but I'll do my best to explain how it is derived.
The outer join combined with the IS NULL check allows you to search for the absence of a criteria. Combine that with the inverse of what you're normally looking for (in this case status = 'A') and the query succeeds when there are no rows that do not match. This is the same as ((there are no rows) OR (all rows match)). Since we know that there are rows due to the other query on the table, all rows must match.