How to use conditional columns values in the same select statement? - sql-server

I have something like
(COMPLEX_EXPRESSION_N stands for a long subquery)
select
ID_Operation,
FirstCheck = CASE WHEN (COMPLEX_EXPRESSION_1)= 0 then 0 else 1 end,
SecondCheck = CASE WHEN (COMPLEX_EXPRESSION_2)= 0 then 0 else 1 end,
ThirdCheck = CASE WHEN (COMPLEX_EXPRESSION_3)= 0 then 0 else 1 end,
AllChecksOk = Case WHEN
(FirstCheck + SecondCheck + Third CHeck = 3)
Then 'OK' Else 'No' End
from
AllOperationsTable
Is it possible to use FirstCheck, SecondCheck, ThirdCheck as I did in the AllChecksOk line?
I am not concerned about performance, this is something that is manually run once a day on a very small number of records, I just want to avoid to create views, tables or temporary tables and keep all in a single select statement.
As an altenrative I can do this, but it makes the query less readable (as I need to write twice every complex expression):
select
ID_Operation,
FirstCheck = CASE WHEN (COMPLEX_EXPRESSION_1)= 0 then 0 else 1 end,
SecondCheck = CASE WHEN (COMPLEX_EXPRESSION_2)= 0 then 0 else 1 end,
ThirdCheck = CASE WHEN (COMPLEX_EXPRESSION_3)= 0 then 0 else 1 end,
AllChecksOk = Case WHEN
(COMPLEX_EXPRESSION_1+ COMPLEX_EXPRESSION_2+
COMPLEX_EXPRESSION_3CHeck = 3) Then 'OK' Else 'No' End
from
AllOperationsTable

You can't reference a column alias in the select but you can use a CTE as below.
;WITH CTE AS
(
select
ID_Operation,
FirstCheck = CASE WHEN (COMPLEX_EXPRESSION_1)= 0 then 0 else 1 end,
SecondCheck = CASE WHEN (COMPLEX_EXPRESSION_2)= 0 then 0 else 1 end,
ThirdCheck = CASE WHEN (COMPLEX_EXPRESSION_3)= 0 then 0 else 1 end
from
AllOperationsTable
)
SELECT *,
AllChecksOk = Case WHEN
(COMPLEX_EXPRESSION_1+ COMPLEX_EXPRESSION_2+
COMPLEX_EXPRESSION_3CHeck = 3) Then 'OK' Else 'No' End
FROM CTE
You can also use CROSS APPLY to define the 3 column aliases then reference them in the main SELECT list as in this example.

Below is a derived table solution
SELECT
T.ID_Operation,
FirstCheck = CASE WHEN T.Expr1 = 0 THEN 0 ELSE 1 END,
SecondCheck = CASE WHEN T.Expr2 = 0 THEN 0 ELSE 1 END,
ThirdCheck = CASE WHEN T.Expr3 = 0 THEN 0 ELSE 1 END,
AllChecksOk = CASE WHEN T.Expr1 + T.Expr2 + T.Expr3 = 3 THEN 'OK' ELSE 'No' END
FROM
(
SELECT
ID_Operation,
Expr1 = (COMPLEX_EXPRESSION_1),
Expr2 = (COMPLEX_EXPRESSION_2),
Expr3 = (COMPLEX_EXPRESSION_3)
FROM
AllOperationsTable
) T

Personally, I find using CTE or derived tables a bit confusing for this purpose, as you have to nest things one level and think about the nesting impliciations. A much simpler approach (at least in my opinion) is to use APPLY (or standard SQL LATERAL in other RDBMS) to generate column expression aliases:
SELECT
ID_Operation,
FirstCheck,
SecondCheck,
ThirdCheck,
AllChecksOk = CASE
WHEN FirstCheck + SecondCheck + ThirdCheck = 3 THEN 'OK' ELSE 'NO'
END
FROM
AllOperationsTable
CROSS APPLY (
SELECT
FirstCheck = CASE WHEN COMPLEX_EXPRESSION_1 = 0 THEN 0 ELSE 1 END,
SecondCheck = CASE WHEN COMPLEX_EXPRESSION_1 = 0 THEN 0 ELSE 1 END,
ThirdCheck = CASE WHEN COMPLEX_EXPRESSION_1 = 0 THEN 0 ELSE 1 END
) t

Related

SQL: Reusing Query with EXIST

Ok so I have a query that looks something like
SELECT
CASE WHEN EXISTS(SELECT NULL
FROM B
WHERE B.LENGTH = A.LENGTH + 10)
THEN 'Yes'
ELSE 'No'
END AS Result1,
CASE WHEN EXISTS(SELECT NULL
FROM B
WHERE B.LENGTH = A.LENGTH - 10)
THEN 'Yes'
ELSE 'No'
END AS Result2,
CASE WHEN EXISTS(SELECT NULL
FROM B
WHERE B.LENGTH = A.LENGTH)
THEN 'Yes'
ELSE 'No'
END AS Result3
FROM A
As you can see the 3 EXIST Queries are almost the same with little differentce that (I hope) can be passed as an argument.
I tried to create a TVF but it fails when I Return SELECT NULL... but works if I use SELECT *. The thing I am afraid of is that I don't need the values I want to check if only exists and that's it.
My question is what would be the best way to refactor this code so it's not that repetitive?
You can use conditional aggregation to generate one query per row having three columns:
SELECT CASE WHEN CA.C1 > 0 THEN 'Yes' ELSE 'No' END
, CASE WHEN CA.C2 > 0 THEN 'Yes' ELSE 'No' END
, CASE WHEN CA.C3 > 0 THEN 'Yes' ELSE 'No' END
FROM A
CROSS APPLY (
SELECT COUNT(CASE WHEN B.LENGTH = A.LENGTH + 10 THEN 1 END)
, COUNT(CASE WHEN B.LENGTH = A.LENGTH - 10 THEN 1 END)
, COUNT(CASE WHEN B.LENGTH = A.LENGTH THEN 1 END)
FROM B
WHERE B.LENGTH IN (A.LENGTH + 10, A.LENGTH - 10, A.LENGTH)
) AS CA(C1, C2, C3)
You could do something like this:
SELECT A.Length,
CASE COUNT(CASE A.[LENGTH] WHEN B.[LENGTH] - 10 THEN 1 END)WHEN 0 THEN 'No' ELSE 'Yes' END AS result1,
CASE COUNT(CASE A.[LENGTH] WHEN B.[LENGTH] + 10 THEN 1 END)WHEN 0 THEN 'No' ELSE 'Yes' END AS result2,
CASE COUNT(CASE A.[LENGTH] WHEN B.[LENGTH] THEN 1 END)WHEN 0 THEN 'No' ELSE 'Yes' END AS result3
FROM A
CROSS JOIN B
GROUP BY A.Length;
This saves on 3 scans of the table B, but you will still need repetitive logic of some kind.

MERGE statement updates even if the data is not updated

I have a merge statement in which I'd like to update my table rows in case any of the columns have a different value. But it seems like even though most of the rows in the source table have remained intact, the MERGE statement performs an UPDATE on at least counts what it does an UPDATE.
DECLARE #SummaryOfChanges TABLE(Change VARCHAR(50));
MERGE MyTarget AS TARGET
USING MySource AS SOURCE
ON (SOURCE.customeridHash = TARGET.Id)
WHEN MATCHED AND (TARGET.IsCompany <> SOURCE.company
OR TARGET.Gender <> SOURCE.gender
OR TARGET.BirthDate <> CONVERT(DATE, SOURCE.dateofbirth)
OR TARGET.ZipCode <> SOURCE.ZipCode
OR TARGET.City <> SOURCE.City
OR TARGET.WantsEmail <> (CASE WHEN SOURCE.noemail = 0 THEN 1 ELSE 0 END)
OR TARGET.WantsSMS <> (CASE WHEN SOURCE.nosms = 0 THEN 1 ELSE 0 END)
OR TARGET.WantsDM <> (CASE WHEN SOURCE.nodirectmarketing = 0 THEN 1 ELSE 0 END)
OR TARGET.WantsTM <> (CASE WHEN SOURCE.notelemarketing = 0 THEN 1 ELSE 0 END)
OR TARGET.HasEmail <> SOURCE.HasEmail
OR TARGET.HasMobilePhoneNumber <> SOURCE.HasMobilePhoneNumber
OR TARGET.HasPhoneNumber = SOURCE.HasPhoneNumber
OR TARGET.Created <> SOURCE.Created
OR TARGET.Updated <> SOURCE.changed)
THEN
UPDATE SET TARGET.IsCompany = SOURCE.company,
TARGET.Gender = SOURCE.gender,
TARGET.BirthDate = CONVERT(DATE, SOURCE.dateofbirth),
TARGET.ZipCode = SOURCE.ZipCode,
TARGET.City = SOURCE.City,
TARGET.WantsEmail = (CASE WHEN SOURCE.noemail = 0 THEN 1 ELSE 0 END),
TARGET.WantsSMS = (CASE WHEN SOURCE.nosms = 0 THEN 1 ELSE 0 END),
TARGET.WantsDM = (CASE WHEN SOURCE.nodirectmarketing = 0 THEN 1 ELSE 0 END),
TARGET.WantsTM = (CASE WHEN SOURCE.notelemarketing = 0 THEN 1 ELSE 0 END),
TARGET.HasEmail = SOURCE.HasEmail,
TARGET.HasMobilePhoneNumber = SOURCE.HasMobilePhoneNumber,
TARGET.HasPhoneNumber = SOURCE.HasPhoneNumber,
TARGET.Created = SOURCE.Created,
TARGET.Updated = SOURCE.changed
WHEN NOT MATCHED BY TARGET THEN
INSERT (
Id,
IsCompany,
Gender,
BirthDate,
ZipCode,
City,
WantsEmail,
WantsSMS,
WantsDM,
WantsTM,
HasEmail,
HasMobilePhoneNumber,
HasPhoneNumber,
Created,
Updated
)
VALUES (
SOURCE.customeridHash,
SOURCE.company,
SOURCE.gender,
CONVERT(DATE, SOURCE.dateofbirth),
SOURCE.ZipCode,
SOURCE.City,
(CASE WHEN SOURCE.noemail = 0 THEN 1 ELSE 0 END),
(CASE WHEN SOURCE.nosms = 0 THEN 1 ELSE 0 END),
(CASE WHEN SOURCE.nodirectmarketing = 0 THEN 1 ELSE 0 END),
(CASE WHEN SOURCE.notelemarketing = 0 THEN 1 ELSE 0 END),
SOURCE.HasEmail,
SOURCE.HasMobilePhoneNumber,
SOURCE.HasPhoneNumber,
SOURCE.Created,
SOURCE.changed
)
WHEN NOT MATCHED BY SOURCE THEN DELETE
OUTPUT $action INTO #SummaryOfChanges;
SELECT Change, COUNT(*) CountPerChange
FROM #SummaryOfChanges
GROUP BY Change;
I do some bookkeeping at the end of the update (the final SELECT) and it seems like almost all the rows that are not new, were updated. Is this a common behavior or is there really a value amongst my <> comparison for WHEN MATCHED AND... that is updated?
Update: As suggested by one of the comments, I wrote the following test to check whether my conditions trigger an update or not:
-- TEST MERGE
select count(*)
from MyTarget TARGET join MySource SOURCE on TARGET.Id=SOURCE.customeridHash
where TARGET.IsCompany <> SOURCE.company
OR TARGET.Gender <> SOURCE.gender
OR TARGET.BirthDate <> CONVERT(DATE, SOURCE.dateofbirth)
OR TARGET.ZipCode <> SOURCE.ZipCode
OR TARGET.City <> SOURCE.City
OR TARGET.WantsEmail <> (CASE WHEN SOURCE.noemail = 0 THEN 1 ELSE 0 END)
OR TARGET.WantsSMS <> (CASE WHEN SOURCE.nosms = 0 THEN 1 ELSE 0 END)
OR TARGET.WantsDM <> (CASE WHEN SOURCE.nodirectmarketing = 0 THEN 1 ELSE 0 END)
OR TARGET.WantsTM <> (CASE WHEN SOURCE.notelemarketing = 0 THEN 1 ELSE 0 END)
OR TARGET.HasEmail <> SOURCE.HasEmail
OR TARGET.HasMobilePhoneNumber <> SOURCE.HasMobilePhoneNumber
OR TARGET.HasPhoneNumber = SOURCE.HasPhoneNumber
OR TARGET.Created <> SOURCE.Created
OR TARGET.Updated <> SOURCE.changed;
I realised that this query returns the same number of updates. So it is somehow more about the condition that the MERGE statement. But I wonder how they trigger updates.
I think I found my own mistake, in the conditions I write:
OR TARGET.HasPhoneNumber = SOURCE.HasPhoneNumber
Which almost always renders true!

Select with Case - amalgamating results into single record

I have a query as follows
SELECT
FGB.TEMPLATE_DETAILS_REF, FGB.TEMPLATE_STRUCTURE_REF, FGB.DFEE_ELEMENT,
CASE
WHEN Condition = 'A' THEN SUM(FGS.Weighted) END AS CondA,
CASE
WHEN Condition = 'B' THEN SUM(FGS.Weighted) END AS CondB,
CASE
WHEN Condition = 'C' THEN SUM(FGS.Weighted) END AS CondC,
CASE
WHEN Condition = 'D' THEN SUM(FGS.Weighted) END AS CondD
FROM FGBlockSummary AS FGB INNER JOIN
FGSurveyItem AS FGS ON FGB.TEMPLATE_DETAILS_REF = FGS.TEMPLATE_DETAILS_REF AND FGB.TEMPLATE_STRUCTURE_REF = FGS.TEMPLATE_STRUCTURE_REF AND
FGB.DFEE_ELEMENT = FGS.DFEE_ELEMENT
GROUP BY FGB.TEMPLATE_DETAILS_REF, FGB.TEMPLATE_STRUCTURE_REF, FGB.DFEE_ELEMENT, FGS.Condition
which produces results as follows:
16 109 Ceilings NULL 14101.47 NULL NULL
16 109 Ceilings NULL NULL 227.68 NULL
How can I amalgamate the results into a single row eg
16 109 Ceilings NULL 14101.47 227.68 NULL
Thanks
Is this what you're looking for?
I moved the SUM outside of the CASEs and eliminated FGS.Condition from the GROUP BY. It'll have the side-effect of changing your NULL values to '0', but that might be tolerable?
SELECT
FGB.TEMPLATE_DETAILS_REF
,FGB.TEMPLATE_STRUCTURE_REF
,FGB.DFEE_ELEMENT
,CondA = SUM(
CASE
WHEN FGS.Condition = 'A' THEN FGS.Weighted
ELSE 0
END)
,CondB = SUM(
CASE
WHEN FGS.Condition = 'B' THEN FGS.Weighted
ELSE 0
END)
,CondC = SUM(
CASE
WHEN FGS.Condition = 'C' THEN FGS.Weighted
ELSE 0
END)
,CondD = SUM(
CASE
WHEN FGS.Condition = 'D' THEN FGS.Weighted
ELSE 0
END)
FROM
FGBlockSummary AS FGB
INNER JOIN
FGSurveyItem AS FGS
ON
FGB.TEMPLATE_DETAILS_REF = FGS.TEMPLATE_DETAILS_REF
AND FGB.TEMPLATE_STRUCTURE_REF = FGS.TEMPLATE_STRUCTURE_REF
AND FGB.DFEE_ELEMENT = FGS.DFEE_ELEMENT
GROUP BY
FGB.TEMPLATE_DETAILS_REF
,FGB.TEMPLATE_STRUCTURE_REF
,FGB.DFEE_ELEMENT;

TSQL turning these multiple CASE queries into one query

I have the following CASE queries that return 1 if they find anything, and 0 otherwise. I would like to turn these into a single query that returns 1 if any of them are true, or 0 otherwise. How can I acomplish this?
SELECT CASE WHEN count(PLACE.CODE) > 0 THEN 1 ELSE 0 END
FROM PLACE
WHERE STYLE = 'RED'
AND RULES = 'NO'
SELECT CASE WHEN count(GARDEN.AREA) > 0 THEN 1 ELSE 0 END
FROM GARDEN
WHERE PLACE = 'GROUND'
AND MAZE = '1'
SELECT CASE WHEN count(place_area.AVAILABLE_AREA) > 0 THEN 1 ELSE 0 END
FROM PLACE_AREA as place_area
INNER JOIN USED_PLACE as used_place
ON used_place.COLOR = 'RED'
AND used_place.MAKE = 'INDUSTRY'
WHERE place_area.CODE = 'FLOOR'
AND place_area.DANCE = '0'
If I understand you correctly, you can union all the result and calculate the sum, if sum > 0 then there was at least one 1
Select Case When Sum(x.col) > 0 THEN 1 ELSE 0 END from
(
SELECT CASE WHEN count(PLACE.CODE) > 0 THEN 1 ELSE 0 END as col
FROM PLACE
WHERE STYLE = 'RED'
AND RULES = 'NO'
Union All
SELECT CASE WHEN count(GARDEN.AREA) > 0 THEN 1 ELSE 0 END as col
FROM GARDEN
WHERE PLACE = 'GROUND'
AND MAZE = '1'
Union All
SELECT CASE WHEN count(place_area.AVAILABLE_AREA) > 0 THEN 1 ELSE 0 END as col
FROM PLACE_AREA as place_area
INNER JOIN USED_PLACE as used_place
ON used_place.COLOR = 'RED'
AND used_place.MAKE = 'INDUSTRY'
WHERE place_area.CODE = 'FLOOR'
AND place_area.DANCE = '0'
) x
SELECT
CASE
WHEN
EXISTS(
SELECT TOP 1 1
FROM PLACE
WHERE STYLE = 'RED'
AND RULES = 'NO'
AND count(PLACE.CODE) > 0
)
OR EXISTS (
SELECT TOP 1 1
FROM GARDEN
WHERE PLACE = 'GROUND'
AND MAZE = '1'
AND count(GARDEN.AREA) > 0
)
OR EXISTS (
SELECT TOP 1 1
FROM PLACE_AREA as place_area
INNER JOIN USED_PLACE as used_place
ON used_place.COLOR = 'RED'
AND used_place.MAKE = 'INDUSTRY'
WHERE place_area.CODE = 'FLOOR'
AND place_area.DANCE = '0'
AND count(place_area.AVAILABLE_AREA) > 0
)
THEN 1
ELSE 0
END

SQL Server CASE - WHEN - ELSE

I have 2 tables like this:
Table CT: Number, CtID, Date, CTIE
Table VT: Number, VtID, Quantities
And my code:
SELECT
MAX(CT.Date), MAX(CT.CtID), VT.VtID,
SUM(VT.Quantities) AS SumVT,
CASE
WHEN CT.CTIE = 0 THEN SUM(VT.Quantities)
ELSE 0
END AS IMPORT,
CASE
WHEN CT.CTIE = 1 THEN SUM(VT.Quantities)
ELSE 0
END AS EXPORT
FROM
CT
INNER JOIN
VT ON CT.Number = VT.Number
GROUP BY
VT.VtID, CT.CTIE
ORDER BY
VT.VtID
This code works fine but the result is not what I want. With some VtID that have both CTIE = 1 and CTIE = 1, SQL now returns 2 separate rows with same VtID, one for CTIE = 0 and one for CTIE = 1. But I need it to display only 1 row for each VtID instead of 2.
Remove CT.CTIE in the group by and place your case statements inside the aggregate function
SElECT
MAX(CT.Date), MAX(CT.CtID), VT.VtID, SUM(VT.Quantities) AS SumVT,
SUM(CASE WHEN CT.CTIE = 0 THEN VT.Quantities ELSE 0 END) AS IMPORT,
SUM(CASE WHEN CT.CTIE = 1 THEN VT.Quantities ELSE 0 END) AS EXPORT
FROM CT INNER JOIN VT ON CT.Number=VT.Number
GROUP BY VT.VtID
ORDER by VT.VtID

Resources