I am working on tuning a stored procedure. The stored procedure is doing a SELECT and has around 50 WHERE conditions. The SQL is similar to below. Can you please let me know if there is a better way to check these conditions
SELECT * FROM table A
JOIN table B
ON A.ID = B.ID
WHERE
(
(
A.name = 'abc'
and B.AID not in ( 111, 222)
)
or
(A.name = 'def'
and B.AID not in ( 222,1113,111,654,199,43,243,643,754244,2434)
)
or
(
A.name = 'etd'
and B.AID not in ( 111,345,54,34,454)
)
or
(
A.name = 'ent'
and B.AID not in ( 111,188,199,1647,128006)
)
or
(
A.name = 'yyy'
and B.AID not in (111,188,1113,1647)
)
or
(
A.name = 'uuu'
and B.AID not in (111,188,1113,1647)
)
or
(
A.name = 'jyf'
and B.AID not in (111,188,1647,344,45444,645)
)
or
(
A.name = 'tut'
and B.AID not in (111,222,1113,1647)
)
)
Create a table to map the names and IDs:
Name AID
------ ------
abc 111
abc 222
def 222
def 1113
..etc
Use a LEFT join to exclude matches:
SELECT * FROM table A
JOIN table B
ON A.ID = B.ID
LEFT JOIN Exclusions e
ON A.name = e.name
and B.AID = e.AID
WHERE e.name IS NULL -- not in exclusion table
or a NOT EXISTS:
SELECT * FROM table A
JOIN table B
ON A.ID = B.ID
WHERE NOT EXISTS(
SELECT null FROM Exclusions e
WHERE A.name = e.name
and B.AID = e.AID
)
Related
I need to get the result of subtract values from these 3 different tables in SQL Server.
This is my SQL:
SELECT COUNT(A.Id)
FROM Table_A AS A WITH (NOLOCK)
WHERE A.City = 'NewYork'
SELECT COUNT(B.Id)
FROM Table_B AS B WITH (NOLOCK)
WHERE B.City = 'England'
SELECT COUNT(C.Id)
FROM Table_C AS C WITH (NOLOCK)
WHERE C.City = 'Berlin'
Let's say the result of the first query is 9, and the second one is 1, and the third one is 3.
I need to get (9-1-3 = 5). How can I do this?
You can do this with a CTE or with subqueries.
Using a CTE:
WITH tbla AS (
SELECT COUNT(A.Id) A
FROM Table_A AS A
WHERE A.City = 'NewYork'
),
tblb AS (
SELECT COUNT(B.Id) B
FROM Table_B AS B
WHERE B.City = 'England'
),
tblc AS (
SELECT COUNT(C.Id) C
FROM Table_C AS C
WHERE C.City = 'Berlin'
)
SELECT a.A - b.B - c.C
FROM tbla a
CROSS JOIN tblb b
CROSS JOIN tblc c;
Same thing only with subqueries
SELECT a.A - b.B - c.C
FROM (
SELECT COUNT(A.Id) A
FROM Table_A AS A
WHERE A.City = 'NewYork'
) a
CROSS JOIN (
SELECT COUNT(B.Id) B
FROM Table_B AS B
WHERE B.City = 'England'
) b
CROSS JOIN (
SELECT COUNT(C.Id) C
FROM Table_C AS C
WHERE C.City = 'Berlin'
) c;
Note: I removed the WITH (NOLOCK) hints because in all likelihood they are not needed in this case.
As all queries return a scalar value 8one row one column you can simply add or subtract them
SELECT
(SELECT
COUNT(A.Id)
FROM
Table_A AS A
WHERE
A.City = 'NewYork') - (SELECT
COUNT(B.Id)
FROM
Table_B AS B
WHERE
B.City = 'England') - (SELECT
COUNT(C.Id)
FROM
Table_C AS C
WHERE
C.City = 'Berlin') as Whatever
I need to concatinate results from 2 tables without using UNION.
Ex : I have 4 tables a, b, c, d. see below snips:
Table a:
Table b:
Table c:
Table d:
I am concatinating a and d results using UNION ALL like below:
select a.id,a.seq,a.item,b.des,c.qty from a
left join b on a.item = b.item
left join c on a.id = c.id and a.seq = c.seq
UNION ALL
select d.id,d.seq,d.item,d.des,c.qty from d
join c on d.id = c.id and d.seq = c.seq
My output:
But I need same result without using UNION ALL.
Is it possible if so HOW?
You can apply Full Outer join instead of Union which has its similarities to Union
SELECT a.id,a.seq,a.item,b.des,c.qty
FROM a left join b on a.item = b.item
left join c on a.id = c.id and a.seq = c.seq
FULL OUTER JOIN
(
SELECT d.id,d.seq,d.item,d.des,c.qty
FROM d join c on d.id = c.id and d.seq = c.seq
)x ON a.id = x.id
You can replace union all with full join with a "false" condition and lots of COALESCE()s.
The logic looks like this:
SELECT COALESCE(abc.id, cd.id) as id,
COALESCE(abc.seq, cd.seq) as seq,
COALESCE(abc.item, cd.item) as item,
COALESCE(abc.des, cd.des) as eesc,
COALESCE(abc.qty, cd.qty) as qty
FROM (SELECT a.id, a.seq, a.item, b.des, c.qty
FROM a LEFT JOIN
b
ON a.item = b.item LEFT JOIN
c
ON a.id = c.id AND a.seq = c.seq
) abc FULL JOIN
(SELECT d.id, d.seq, d.item, d.des, c.qty
FROM d JOIN
c
ON d.id = c.id AND d.seq = c.seq
) dc
ON 1 = 0; -- never evaluates to true
There is a LEFT JOIN from table 'a' to table 'c' (on 'id' and 'seq' columns) in the upper query and INNER JOIN from table 'd' to table 'c' in the lower query. Therefore I think you could LEFT JOIN from table 'c' to table 'd' and it would produce the correct output.
select a.id,a.seq,a.item,b.des,c.qty
from a
left join b on a.item = b.item
left join c on a.id = c.id and a.seq = c.seq
left join d on c.id = d.id and c.seq = d.seq;
If you want you can use INSERT INTO a (tmp) table and get the same result in a table.
Please find the below provided query which I'm able to implement in Netezza where in case there is no join on 'id' then it looks for the join on the basis of 'name'. if any of the criteria gets fulfilled then Left outer join is performed.
Select * from BigqueryTest.colors a
LEFT JOIN
BigqueryTest.color2 b
ON a.id = b.id
OR a.name = b.name ;
This functionality doesn't seem to be supported in Bigquery. I mean to say that I can provide multiple joining conditions with the help of 'AND' operator but can't use 'OR' operator which can allow me moving ahead with Join operation even if a single condition gets satisfied out of many. Any leads would be appreciated.
... is there any way around to make the code run with 'OR' condition? Because in actual scenario there are more than 100 columns ...
Below is another option - as a direction for you to explore.
#standardSQL
SELECT a.id AS aid, a.name AS aname, b.id AS bid, b.name AS bname
FROM (
SELECT
a, IF(matches = 0, STRUCT<id INT64, name STRING>(NULL, NULL), b) AS b
FROM (
SELECT
a, b,
(a.id = b.id OR a.name = b.name) AS match,
COUNTIF(a.id = b.id OR a.name = b.name)
OVER(PARTITION BY a.id, a.name) AS matches,
ROW_NUMBER() OVER(PARTITION BY a.id, a.name) AS dup
FROM `BigqueryTest.colors` AS a
CROSS JOIN `BigqueryTest.color2` AS b
)
WHERE match OR (matches = 0 AND dup = 1)
)
-- ORDER BY a.id
You can test/play with it using below dummy data
#standardSQL
WITH `BigqueryTest.colors` AS (
SELECT 1 AS id, 'a' AS name UNION ALL
SELECT 2 AS id, 'b' AS name UNION ALL
SELECT 3 AS id, 'c' AS name
),
`BigqueryTest.color2` AS (
SELECT 1 AS id, 'a' AS name UNION ALL
SELECT 11 AS id, 'a' AS name UNION ALL
SELECT 2 AS id, 'b' AS name UNION ALL
SELECT 2 AS id, 'x' AS name
)
SELECT a.id AS aid, a.name AS aname, b.id AS bid, b.name AS bname
FROM (
SELECT
a, IF(matches = 0, STRUCT<id INT64, name STRING>(NULL, NULL), b) AS b
FROM (
SELECT
a, b,
(a.id = b.id OR a.name = b.name) AS match,
COUNTIF(a.id = b.id OR a.name = b.name) OVER(PARTITION BY a.id, a.name) AS matches,
ROW_NUMBER() OVER(PARTITION BY a.id, a.name) AS dup
FROM `BigqueryTest.colors` AS a
CROSS JOIN `BigqueryTest.color2` AS b
)
WHERE match OR (matches = 0 AND dup = 1)
)
ORDER BY a.id
Below is for BigQuery Standard SQL
#standardSQL
SELECT
a.id AS aid, a.name AS aname,
b.id AS bid, b.name AS bname
FROM `BigqueryTest.colors` AS a
LEFT JOIN `BigqueryTest.color2` AS b
ON a.id = b.id
UNION DISTINCT
SELECT
a.id AS aid, a.name AS aname,
b.id AS bid, b.name AS bname
FROM `BigqueryTest.colors` AS a
LEFT JOIN `BigqueryTest.color2` AS b
ON a.name = b.name
I have to use a conditional statement in join:
select * from A inner join B
on A.id = B.id
if B.id is null or B.id = '' or A.id is null or A.id = '' it should be A.name = B.name, instead of A.id = B.id
I have to use a conditional statement in join:
select * from A inner join B
on A.id = B.id
if B.id is null or B.id = '' it should be A.id2 = B.id2 instead of A.id = B.id.
Is it correct if I do something like this:
select *
from A inner join B on
(B.id is not null and B.id <> '' and A.id is not null and A.id <> '' and A.id = B.id) or
((B.id is null or B.id = '' or A.id is null or A.id = '') and A.name = B.name)
I think you will want something like this:
select * from A
inner join Bid on
(Bid.id is not null and Bid.id <> '' and A.id is not null and A.id <> '' and A.id = Bid.id)
inner join Bname on
((Bname.id is null or Bname.id = '' or A.id is null or A.id = '') and A.name = Bname.name)
that is to join on the B table twice, one join for when id is not null and the second join when id is null and you join on the name column instead. This works effectively like a case statement because the two joins to the B table are mutually exclusive.
Try this
select * from A inner join B
on (A.id = B.id) or
(A.name = B.name and (B.id is null or B.id = '' or A.id is null or A.id = ''))
try this code
DECLARE #b varchar(50)=(SELECT id FROM b)
DECLARE #a varchar(50)=(SELECT id FROM a)
if ((#b in (null , '')) and (#a in (null , '')))
BEGIN
select * from A inner join B
on
A.name = B.name
END
else
BEGIN
select * from A inner join B
on A.id = B.id
END
SELECT * FROM a
JOIN (SELECT * FROM b WHERE b.aId = a.Id) AS c ON c.aId = a.Id
It says does not recognize: a.Id in the Where Clause.
I know its probably cause im using a temp table and a.Id cannot be passed through but is there any way we can do that?
Because here is what actually happens
SELECT *
FROM a
JOIN (SELECT * FROM b
WHERE b.aId = a.Id
ORDER BY b.dateReg DESC
LIMIT 1) AS c ON c.aId = a.Id
I need the ORDER BY b.dateReg DESC LIMIT 1 as it returns me the last row that assosiates with the a Table.. If you require i can post the Create Query
-- find last rows on b
select * from b x
where exists(
select id
from b y
where y.id = b.id
having max(y.dateReg) = x.dateReg
group by id
)
-- then join that b to a, this is the final query:
select * from a
join
(
select * from b x
where exists(
select id
from b y
where y.id = b.id
having max(y.dateReg) = x.dateReg
group by id
)
) as last_rows on last_rows.id = a.id
-- simpler:
select *
from a join b x on a.id = x.id
where exists(
select id
from b y
where y.id = b.id
having max(y.dateReg) = x.dateReg
group by id)
-- or if you will use postgres:
select DISTINCT ON (a.id) *
from a join b x on a.id = x.id
order by a.id, b.dateReg DESC
-- look ma! no group by!
-- nothing beats postgresql's simplicity :-)
Try:
SELECT DISTINCT *
FROM A
JOIN B b ON b.aid = a.id
JOIN (SELECT b.aid,
MAX(b.datereg) 'max_datereg'
FROM B b
GROUP BY b.aid) md ON md.aid = b.aid
AND md.max_datereg = b.datereg
If you do want the first record associated with the associate, use:
SELECT DISTINCT *
FROM A
JOIN B b ON b.aid = a.id
JOIN (SELECT b.aid,
MIN(b.datereg) 'min_datereg'
FROM B b
GROUP BY b.aid) md ON md.aid = b.aid
AND md.min_datereg = b.datereg