Errors usually but within EXISTS sub-query it runs - sql

Is the following problematic?
DELETE a
FROM WHAnalysis.dbo.tb_r12027dxi_CalculatedData a
WHERE EXISTS
(
SELECT *
FROM WHAnalysis.dbo.tb_r12027dxi_CalculatedData b
WHERE
b.[Past28Days] = 1 AND
a.[Index] = b.[Index]
HAVING SUM(b.Amount) = 0
)
Reason I'm slightly uneasy about using the above script is that if I run the following it errors:
SELECT *
FROM WHAnalysis.dbo.tb_r12027dxi_CalculatedData b
WHERE
b.[Past28Days] = 1
HAVING SUM(b.Amount) = 0
I understand why this script errors => the select is not grouped on anything therefore the processor does not like the aggregation in the HAVING clause.
But as a sub-query this error does not occur - why? Is this a problematic approach?
EDIT
Ended up using the following:
DELETE a
FROM WHAnalysis.dbo.tb_r12027dxi_CalculatedData a
WHERE a.[Index] IN
(
SELECT [Index]
FROM WHAnalysis.dbo.tb_r12027dxi_CalculatedData
WHERE [Past28Days] = 1
GROUP BY [Index]
HAVING SUM(Amount) = 0
)
But as suggested in answer the following is more readable by simply adding the GROUP BY into the sub-query:
DELETE a
FROM WHAnalysis.dbo.tb_r12027dxi_CalculatedData a
WHERE EXISTS
(
SELECT *
FROM WHAnalysis.dbo.tb_r12027dxi_CalculatedData b
WHERE
b.[Past28Days] = 1 AND
a.[Index] = b.[Index]
GROUP BY b.[Index]
HAVING SUM(b.Amount) = 0
)

It is legal to omit group by and still perform aggregations, therefore having is still a way of limiting results:
select sum(x)
from
(
select 1 x union all select 2
) a
having sum(x) = 3
Exists() work because everything in select list is ignored. Exists() looks for rows only, and terminates as soon as one is found. You might add group by b.Index to make intent clear to anyone reviewing the code later, or rewrite it using inner join and derived table.
DELETE a
FROM WHAnalysis.dbo.tb_r12027dxi_CalculatedData a
INNER JOIN
(
SELECT b.[Index]
FROM WHAnalysis.dbo.tb_r12027dxi_CalculatedData b
WHERE
b.[Past28Days] = 1
GROUP BY b.[Index]
HAVING SUM(b.Amount) = 0
) b1
ON a.[Index] = b1.[Index]

Related

SELECT NOT IN with multiple columns in subquery

Regarding the statement below, sltrxid can exist as both ardoccrid and ardocdbid. I'm wanting to know how to include both in the NOT IN subquery.
SELECT *
FROM glsltransaction A
INNER JOIN cocustomer B ON A.acctid = B.customerid
WHERE sltrxstate = 4
AND araccttype = 1
AND sltrxid NOT IN(
SELECT ardoccrid,ardocdbid
FROM arapplyitem)
I would recommend not exists:
SELECT *
FROM glsltransaction t
INNER JOIN cocustomer c ON c.customerid = t.acctid
WHERE
??.sltrxstate = 4
AND ??.araccttype = 1
AND NOT EXISTS (
SELECT 1
FROM arapplyitem a
WHERE ??.sltrxid IN (a.ardoccrid, a.ardocdbid)
)
Note that I changed the table aliases to things that are more meaningful. I would strongly recommend prefixing the column names with the table they belong to, so the query is unambiguous - in absence of any indication, I represented this as ?? in the query.
IN sometimes optimize poorly. There are situations where two subqueries are more efficient:
SELECT *
FROM glsltransaction t
INNER JOIN cocustomer c ON c.customerid = t.acctid
WHERE
??.sltrxstate = 4
AND ??.araccttype = 1
AND NOT EXISTS (
SELECT 1
FROM arapplyitem a
WHERE ??.sltrxid = a.ardoccrid
)
AND NOT EXISTS (
SELECT 1
FROM arapplyitem a
WHERE ??.sltrxid = a.ardocdbid
)

SQL query having CASE WHEN EXISTS statement

I trying to create a SQL query with a CASE WHEN EXISTS clause in SQL Server. I assume I am doing something wrong as when I run the SELECT * FROM [Christmas_Sale] it takes forever for SQL to load the code.
CREATE VIEW [Christmas_Sale]
AS
SELECT
C.*,
CASE
WHEN EXISTS (SELECT S.Sale_Date
FROM [Christmas_Sale] s
WHERE C.ID = S.ID)
THEN 0
ELSE 1
END AS ChristmasSale
FROM
[Customer_Detail] C ;
I'm trying to write a sub select which I need to return a 1 if Sale_Date= 1 and 0 for anything else.
The syntax of your query looks ok. But since your stated:
I'm trying to write a sub select which I need to return a 1 if Sale_Date= 1 and 0 for anything else.
... Then you could rephrase your query by adding one more condition in the WHERE clause of the subquery:
CREATE VIEW [Christmas_Sale]AS
SELECT
C.*,
CASE WHEN EXISTS (
SELECT 1
FROM [Christmas_Sale] s
WHERE C.ID = S.ID and S.Sale_Date = 1
) THEN 0 ELSE 1 END AS ChristmasSale
FROM [Customer_Detail] C ;
If a record exists in [Christmas_Sale] with the corresponding ID and Sale_Date = 1, then ChristmasSale will have value 1, else it will display 0.
This query looks correct:
CREATE VIEW [Christmas_Sale] AS
SELECT C.*,
(CASE WHEN EXISTS (SELECT 1
FROM [Christmas_Sale] s
WHERE C.ID = S.ID
)
THEN 0 ELSE 1
END) AS ChristmasSale
FROM [Customer_Detail] C ;
If performance is an issue, you want an index on Christmas_Sale(ID).
Note that the SELECT S.Sale_Date in the subquery is meaningless, because EXISTS checks for rows not columns. Hence, I replaced it with the simpler 1.

Select values where tow different conditions

I have a Questions group table like follows:
ID, NAME, DESCRIPTION, VERSION_ID
Versions table columns are:
ID, NUMBER, VERSION_STATE
VERSION_STATE is an enumerated that can be 0, 1 or 2.
I need to select all questions group that its version has thevVERSION_STATE 0 or 1, but if there is a questions group with a VERSION_STATE = 0 I don't have to return the questions group with the VERSION_STATE = 1.
The simplest approach is:
SELECT distinct QG.id FROM healthsafety.hs_questions_group QG
LEFT OUTER JOIN (SELECT * FROM healthsafety.hs_version) VERSION
ON QG.VERSION_ID = VERSION.ID
WHERE
VERSION.VERSION_STATE=0
OR VERSION.VERSION_STATE=1
The problem is that this query returns all questions group with the VERSION_STATE 0 or 1. If I remove the or clause, and there are not questions groups with VERSION_STATE = 0, I need to return the questions groups with VERSION_STATE = 1.
I think that I need an if else or case statement but I am stucked. Any Idea?
Note that I have to implement this using criteria, so I need to use the simplest solution.
SELECT *
FROM (
SELECT OG.ID,OG.NAME,OG.DESCRIPTION,VERSION.ID,VERSION.NUMBER,VERSION.VERSION_STATE,ROW_NUMBER()OVER(PARTITION BY OG.ID ORDER BY VERSION.VERSION_STATE ASC) as INDICATOR
FROM healthsafety.hs_questions_group QG
LEFT OUTER JOIN
(SELECT * FROM healthsafety.hs_version) VERSION
ON QG.VERSION_ID = VERSION.ID
WHERE
VERSION.VERSION_STATE=0
OR VERSION.VERSION_STATE=1
) AS ABC
WHERE (ABC.VERSION_STATE = 0 and ABC.INDICATOR = 1)
OR (ABC.VERSION_STATE = 1 and ABC.INDICATOR = 1)
Using a common table expression and a union all where the second query uses not exists() to only return rows where version_state=1 when rows with version_state=0 do not exist.
;with cte as (
select qg.id, v.version_state
from healthsafety.hs_questions_group qg
inner join healthsafety.hs_version version v
on qg.version_id = v.id
where v.version_state in (0,1)
)
select id
from cte
where version_state = 0
union all
select qg.id
from cte
where version_state = 1
and not exists (
select 1
from cte
where version_state = 0
)

sql function case returns more than one row

Going to use this query as a subquery, the problem is it returns many rows of duplicates. Tried to use COUNT() instead of exists, but it still returns a multiple answer.
Every table can only contain one record of superRef.
The below query I`ll use in SELECT col_a, [the CASE] From MyTable
SELECT CASE
WHEN
EXISTS (SELECT 1 FROM A WHERE
A_superRef = myTable.sysno AND A_specAttr = 'value')
THEN 3
WHEN EXISTS (SELECT 1 FROM B
INNER JOIN С ON С_ReferenceForB = B_sysNo WHERE C_superRef = myTable.sysno AND b_type = 2)
THEN 2
ELSE (SELECT C_intType FROM C
WHERE C_superRef = myTable.sysno)
END
FROM A, B, C
result:
3
3
3
3
3
3...
What if you did this? Because Im guessing you are getting an implicit full outer join A X B X C then running the case statement for each row in that result set.
SELECT CASE
WHEN
EXISTS (SELECT 1 FROM A WHERE
A_superRef = 1000001838012)
THEN 3
WHEN EXISTS (SELECT 1 FROM B
INNER JOIN С ON С_ReferenceForB = B_sysNo AND C_superRef = 1000001838012 )
THEN 2
ELSE (SELECT C_type FROM C
WHERE C_superRef = 1000001838012)
END
FROM ( SELECT COUNT(*) FROM A ) --This is a hack but should work in ANSI sql.
--Your milage my vary with different RDBMS flavors.
DUAL is what I needed, thanks to Thorsten Kettner
SELECT CASE
WHEN
EXISTS (SELECT 1 FROM A WHERE
A_superRef = 1000001838012)
THEN 3
WHEN EXISTS (SELECT 1 FROM B
INNER JOIN С ON С_ReferenceForB = B_sysNo AND C_superRef = 1000001838012 )
THEN 2
ELSE (SELECT C_type FROM C
WHERE C_superRef = 1000001838012)
END
FROM DUAL

SQL Server 2005 - cross apply, tvf, and update/delete

How can I make something like this
select
a.atest, a.btest, a.ctest, b.atest, b.btest
from
test.dbo.rm a(nolock)
cross apply
wat.dbo.ar(a.atest,a.btest) b
where
a.rmd = 9 and a.btest > 0
alter function ar(#atest varchar(25), #btest numeric(19,5))
returns table as
return
(
select atest, btest from test.dbo.rm (nolock)
where rmd = 1 and atest=#atest and btest=#btest
)
with delete statement or update. I don't want to make duplicates so after I choose one b.atest I want to delete the record with b.atest or set b.btest to 0. This query is working on table that contain about 5-10 million of records.. so it must be quick.
Use query without function:
select a.atest,a.btest,a.ctest,b.atest,b.btest
from test.dbo.rm a(nolock)
cross apply (
select top 1 atest, btest
from test.dbo.rm t (nolock)
where t.rmd = 1 and t.atest=a.atest and t.btest=a.btest
)b
where a.rmd = 9 and a.btest > 0
You can also use Left join instead of cross apply:
select *
from (
select a.atest,a.btest,a.ctest,b.atest,b.btest,
row_number() over ( partition by b.atest, b.btest order by b.id) as row
from test.dbo.rm a(nolock)
LEFT JOIN test.dbo.rm b ON b.rmd = 1 and b.atest=a.atest and b.btest=a.btest
where a.rmd = 9 and a.btest > 0
)z
where z.row = 1