SQL: Reusing Query with EXIST - sql-server

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.

Related

Issue with >= in the WHERE clause

I'm at my whit's end with this one, frustrated because I'm sure it's something simple and I need a second set of eyes. I've tried this several different ways and I continue to run into an issue where the query will run for a full 24 hours with no results...
I've narrowed the issue down to this section of the query:
WHERE sub.[Savings (%)] >= 10
I ran into so many issues that I ended up nesting the entire query into a sub query, trying to take most of my calculations out of the "Where" clause, but to no effect.
This is all part of a much larger query, so I'll post the abbreviated portion that is relevant.
Here's the very beginning of the query:
SELECT *
FROM
(
SELECT
LD.Region
,DM.ShortName AS DM
,D.Lcode
,LD.locationname
,D.UnitID
,D.dPlaced
,D.dCancelled
,D.sRentalType
,D.[Quoted Rate]
,Calcs.MinStdRate AS [Current Rate]
,-(Calcs.MinStdRate - D.[Quoted Rate]) AS [Savings ($)]
,((-(Calcs.MinStdRate - D.[Quoted Rate]))/NULLIF(D.[Quoted Rate],0))*100 AS [Savings (%)]
,D.sPlanName
,Calcs.[# Vacant]
FROM...
Then this is the WHERE clause at the very end of the query that causes the issue. The entire query beneath "SELECT * FROM" (shown up above) is called "Sub":
) AS sub
WHERE sub.[Savings (%)] >= 10
AND sub.[Current Rate] <> 0
AND sub.[Quoted Rate] <> 0
When I notate "sub.[Savings (%)] >= 10 AND" out of the query, the whole thing runs in about 5 seconds. With it, it runs for hours on end and never produces results...
What am I missing?
Updating to show whole query per request:
SELECT *
FROM
(
SELECT
LD.Region
,DM.ShortName AS DM
,D.Lcode
,LD.locationname
,D.UnitID
,D.dPlaced
,D.dCancelled
,D.sRentalType
,D.[Quoted Rate]
,Calcs.MinStdRate AS [Current Rate]
,-(Calcs.MinStdRate - D.[Quoted Rate]) AS [Savings ($)]
,CAST(((-(Calcs.MinStdRate - D.[Quoted Rate]))/NULLIF(D.[Quoted Rate],0))*100 AS DECIMAL (18,2)) AS [Savings (%)]
,D.sPlanName
,Calcs.[# Vacant]
FROM
(SELECT
s.sLocationCode AS lcode
,w.dPlaced
,L.dLease
,u.UnitID
,CASE
WHEN w.dCancelled IS NOT NULL THEN w.dCancelled
WHEN (w.dCancelled IS NULL AND w.dExpires <GETDATE()) THEN w.dExpires
ELSE NULL
END AS dCancelled
,CASE
WHEN (w.dCancelled IS NOT NULL OR w.dExpires <GETDATE()) THEN 'Lost'
WHEN w.QTRentalTypeID = 3 THEN 'Rented'
WHEN (w.QTRentalTypeID = 2 OR w.QTRentalTypeID = 1) THEN 'Active'
END AS sRentalType
,w.dcRate_Quoted AS 'Quoted Rate'
,c.sPlanName
,CONCAT(U.dcWidth,'x',U.dcLength) AS sSize
,UT.sTypeName
FROM CompanyDBs.dbo.waitings AS W
LEFT OUTER JOIN CompanyDBs.dbo.Ledgers AS L
ON W.LedgerID = L.LedgerID
JOIN CompanyDBs.dbo.sites AS S
ON W.SiteID = S.SiteID
JOIN CompanyDBs.dbo.units AS U
ON W.UnitID1 = U.UnitID
JOIN CompanyDBs.dbo.UnitTypes AS UT
ON U.UnitTypeID = UT.UnitTypeID
JOIN CompanyDBs.dbo.ConcessionPlans AS C
ON W.ConcessionID = C.ConcessionID
WHERE W.dCancelled < GETDATE() AND W.dCancelled >= DATEADD(DD,-60,CAST(GETDATE() AS DATE))
UNION ALL
SELECT
s.sLocationCode AS lcode
,w.dPlaced
,L.dLease
,u.UnitID
,CASE
WHEN w.dCancelled IS NOT NULL THEN w.dCancelled
WHEN (w.dCancelled IS NULL AND w.dExpires <GETDATE()) THEN w.dExpires
ELSE NULL
END AS dCancelled
,CASE
WHEN (w.dCancelled IS NOT NULL OR w.dExpires <GETDATE()) THEN 'Lost'
WHEN w.QTRentalTypeID = 3 THEN 'Rented'
WHEN (w.QTRentalTypeID = 2 OR w.QTRentalTypeID = 1) THEN 'Active'
END AS sRentalType
,w.dcRate_Quoted AS 'Quoted Rate'
,c.sPlanName
,CONCAT(U.dcWidth,'x',U.dcLength) AS sSize
,UT.sTypeName
FROM CompanyDBs1.dbo.waitings AS W
LEFT OUTER JOIN CompanyDBs1.dbo.Ledgers AS L
ON W.LedgerID = L.LedgerID
JOIN CompanyDBs1.dbo.sites AS S
ON W.SiteID = S.SiteID
JOIN CompanyDBs1.dbo.units AS U
ON W.UnitID1 = U.UnitID
JOIN CompanyDBs1.dbo.UnitTypes AS UT
ON U.UnitTypeID = UT.UnitTypeID
JOIN CompanyDBs1.dbo.ConcessionPlans AS C
ON W.ConcessionID = C.ConcessionID
WHERE W.dCancelled < GETDATE() AND W.dCancelled >= DATEADD(DD,-60,CAST(GETDATE() AS DATE))) AS D
LEFT OUTER JOIN
(SELECT
P.SiteID
,P.sLocationCode
,P.UnitID
,p.UnitCODE
,groupeddata.[# Vacant]
,GroupedData.MinStdRate
FROM
(SELECT
S2.sLocationCode
,S2.SiteID
,R2.UnitID
,CONCAT(S2.sLocationCode
,'-'
,S2.sSiteName
,' '
,R2.stypename
,' '
,CASE WHEN R2.iFloor > 1 THEN 'Up ' WHEN R2.ifloor < 1 THEN 'Down ' WHEN R2.ifloor = 1 THEN '1 ' END
,CASE WHEN R2.bPower = 1 THEN 'Power ' ELSE 'No Power ' END
,CASE WHEN R2.bClimate = 1 THEN 'CC ' ELSE 'No CC ' END
,CASE WHEN R2.bInside = 1 THEN 'In ' ELSE 'Out ' END
,CASE WHEN R2.bAlarm = 1 THEN 'Alarm ' ELSE 'No Alarm ' END
,CAST(R2.dcWidth AS FLOAT)
,'x'
,CAST(R2.dcLength AS FLOAT)) AS UnitCODE
FROM Operations.dbo.RentRoll AS R2
JOIN
(
SELECT *
FROM CompanyDBs.dbo.sites
UNION ALL
SELECT *
FROM CompanyDBs1.dbo.sites
) AS S2
ON R2.siteid = S2.SiteID
WHERE
R2.ddeleted is NULL
AND R2.bRentable = 1
AND R2.brented = 0 ) AS P
JOIN
(
SELECT
S.SiteID
,s.sLocationCode
,UnitCode.UnitCODE
,SUM(CASE
WHEN R.brented = 1 THEN 0
ELSE 1
END) AS [# Vacant]
,MIN(R.dcStdRate) AS MinStdRate
FROM Operations.dbo.RentRoll AS R
JOIN
(
SELECT *
FROM CompanyDBs.dbo.sites
UNION ALL
SELECT *
FROM CompanyDBs1.dbo.sites
) AS S
ON R.siteid = S.SiteID
JOIN
(
SELECT
S1.SiteID
,UnitID
,CONCAT(S1.sLocationCode
,'-'
,S1.sSiteName
,' '
,R1.stypename
,' '
,CASE WHEN R1.iFloor > 1 THEN 'Up ' WHEN R1.ifloor < 1 THEN 'Down ' WHEN R1.ifloor = 1 THEN '1 ' END
,CASE WHEN R1.bPower = 1 THEN 'Power ' ELSE 'No Power ' END
,CASE WHEN R1.bClimate = 1 THEN 'CC ' ELSE 'No CC ' END
,CASE WHEN R1.bInside = 1 THEN 'In ' ELSE 'Out ' END
,CASE WHEN R1.bAlarm = 1 THEN 'Alarm ' ELSE 'No Alarm ' END
,CAST(R1.dcWidth AS FLOAT)
,'x'
,CAST(R1.dcLength AS FLOAT)) AS UnitCODE
FROM Operations.dbo.RentRoll AS R1
JOIN
(
SELECT *
FROM CompanyDBs.dbo.sites
UNION ALL
SELECT *
FROM CompanyDBs1.dbo.sites
) AS S1
ON R1.siteid = S1.SiteID
) AS UnitCode
ON CONCAT(R.siteid, R.unitid) = CONCAT(UnitCode.siteid,UnitCode.UnitID)
WHERE
s.sLocationCode <> 'L003'
AND s.sLocationCode <> 'L021'
AND s.sLocationCode <> 'LSETUP'
AND s.sLocationCode <> 'LTRAIN'
AND R.bRented = 0
GROUP BY s.siteid, s.slocationcode, UnitCode.UnitCODE
) AS GroupedData
ON P.UnitCODE = GroupedData.UnitCODE
) AS Calcs
ON CONCAT(D.lcode,D.unitid) = CONCAT(Calcs.slocationcode,Calcs.unitid)
JOIN operations.dbo.westport_locationdata AS LD
ON D.lcode = ld.lcode
JOIN operations.dbo.Westport_DMs AS DM
ON LD.DMID = DM.DMID
) AS sub
WHERE sub.[Savings (%)] >= CAST(10.0 AS DECIMAL (18,2))
AND sub.[Current Rate] <> 0
AND sub.[Quoted Rate] <> 0
Unfortunately you are posting not the whole query. I could image that some indexes are missing.
I give you the strong advice to run your query in SQL Server Management Studio with the option "Query" -> "Include Actual Execution Plan" checked.
With this, SSMS will execute the query and tell you afterwards if indexes are missing and how much you could improve by setting them. You will get also a picture oh how the query is running.
Cast your where clause filter value WHERE sub.[Savings (%)] >= cast(10.0 as decimal(18,2)) so there isn't an implicit conversion from int to decimal.
https://learn.microsoft.com/en-us/sql/t-sql/data-types/data-type-conversion-database-engine?view=sql-server-ver15#:~:text=Implicit%20conversions%20are%20not%20visible,converts%20to%20date%20style%200.

Why doesn't count function return accurate results?

I have 2 problems in this query:
SELECT DATENAME(MONTH, pd.PaymentDate) + ' ' + DATENAME(YEAR, pd.PaymentDate) AS PDate,
SUM(Case when PT.PropertyTypeName = 'Commercial' then PD.PropertyDetailsTotalAmount else 0 END) as CommercialIncome,
SUM(Case when PT.PropertyTypeName = 'Residential' then PD.PropertyDetailsTotalAmount else 0 END) as ResidentialIncome,
COUNT(Case when PT.PropertyTypeName = 'Commercial' then PD.pk_PropertyDetails_PropertyDetailsID else 0 END) as TotalCommercialMaps,
COUNT(Case when PT.PropertyTypeName = 'Residential' then PD.pk_PropertyDetails_PropertyDetailsID else 0 END) as TotalResidentialMaps
From PropertyDetails PD
Inner Join Properties P
ON PD.fk_Properties_ID= p.pk_Properties_ID
Inner Join PropertyTypes PT
ON PT.pk_PropertyTypes_PropertyTypeID= P.fk_PropertyTypes_ID
where
PD.Paid= 1
--and PaymentDate >= #DateFrom AND PaymentDate < DATEADD(day,1,#DateTo)
Group By DATENAME(MONTH, pd.PaymentDate) + ' ' + DATENAME(YEAR, pd.PaymentDate)
Problems:
The count lines doesn't return the actual count, it even returns same count even if I write something else instead of 'Commercial/Residential'
I want to get the sum of both counts i.e. TotalCommercialResidentialMaps but when i do that, it throws error:
Cannot perform an aggregate function on an expression containing an aggregate or a subquery.
I tried a lot but doesn't work.
COUNT(X) counts 0 so these expressions:
COUNT(Case when PT.PropertyTypeName = 'Commercial' then PD.pk_PropertyDetails_PropertyDetailsID else 0 END)
will count all rows, assuming PD.pk_PropertyDetails_PropertyDetailsID is non-null.
To get COUNT(X) to not count a row, X will need to be NULL.
So change your case expressions to:
COUNT(Case when .... else NULL end)
^^^^
This should give you your expected results.
The answer that Lasse V. Karlsen has given is correct. Another idiom that you might see that accomplishes the same thing is:
SUM(Case when PT.PropertyTypeName = 'Commercial' then 1 else 0 end)
I like to think of this as going through the result creating tally marks and summing them up is equivalent to counting them.

SQL Server - What's the right query for this purpose?

I have a TABLE that contains, for example:
ID CVG
4 A
4 C
5 B
Each row contains a coverage for a given ID. If the ID does not have a coverage, there will be no row in the table (ex. ID 4 has no coverage B).
From this table I want to write a query like
SELECT ID, A = case (when ??? then 'TRUE' when ??? then 'FALSE'), B = case (...), C = case (...) FROM TABLE
that will give the output
ID A B C
4 TRUE FALSE TRUE
5 FALSE TRUE FALSE
where there is only one row for each ID, and one column for each coverage.
What's a workable query for this? I'm not allowed to change any permanent tables on the server so I'm trying to reach a solution with what I have.
You can use conditional aggregation to get the required result:
SELECT ID,
CASE
WHEN COUNT(CASE WHEN CVG = 'A' THEN 1 END)>=1 THEN 'TRUE'
ELSE 'FALSE'
END AS A,
CASE
WHEN COUNT(CASE WHEN CVG = 'B' THEN 1 END)>=1 THEN 'TRUE'
ELSE 'FALSE'
END AS B,
CASE
WHEN COUNT(CASE WHEN CVG = 'C' THEN 1 END)>=1 THEN 'TRUE'
ELSE 'FALSE'
END AS C,
.... etc
FROM mytable
GROUP BY ID
SQL Fiddle Demo
You could do something like this:
select
ID,
max(case when CVG = 'A' then 'TRUE' else 'FALSE' end),
max(case when CVG = 'B' then 'TRUE' else 'FALSE' end),
max(case when CVG = 'C' then 'TRUE' else 'FALSE' end),
from
table
group by
ID
The case + max will select the true if even one row is found for that ID, otherwise false.
If you want to have it select where
A = case (when ??? then 'TRUE' when ??? then 'FALSE'), B = case (...), C = case (...)
which is what I got from your explenation, what you need to do is
SELECT id, A, B, C FROM TABLE WHERE A = case (when ??? then 'TRUE' when ??? then 'FALSE'), B = case (...), C = case (...)

Issue regarding Nested CASE statement in sql server 2005

here is my sql when i am trying to execute it then i am getting error msg An expression of non-boolean type specified in a context where a condition is expected, near 'then'.
i am not being able to understand it where i am making mistake. so please have a look at my full sql specially where i use nested case and tell me what to rectify.
SELECT
[bbajobs].[jid],
[Add Dates],
CASE WHEN 1 then 'Yes'
CASE WHEN 0 then
CASE WHEN job_flow_state.no_fault_found = 1 THEN 'No fault found'
CASE WHEN job_flow_state.unable_to_repaired = 1 THEN 'Unable to repair'
CASE WHEN job_flow_state.Repair_Not_Requested = 1 THEN 'Repair Not Requested'
ELSE 'N/A'
END
END AS [Repaired]
FROM bbajobs
LEFT JOIN ourfeedback
ON bbajobs.jid = ourfeedback.jid
INNER JOIN job_flow_state
ON bbajobs.jid = job_flow_state.jid
WHERE CONVERT(VARCHAR(8), bbajobs.jobshippeddate, 112) >='20140117'
AND CONVERT(VARCHAR(8), bbajobs.jobshippeddate, 112) <='20140117' AND bbajobs.jobstate IN ('DONE')
AND bbajobs.jobtype NOT LIKE '%warranty%'
AND job_flow_state.repaired = 1
AND (ltrim(rtrim(ourfeedback.Rating))='' OR ltrim(rtrim(ourfeedback.Rating))='N/A') AND [bbajobs].[accountreference] IN
(SELECT accountref FROM ourfeedback where
CONVERT(VARCHAR(8), ourfeedback.adddates, 112) >='20140117' AND CONVERT(VARCHAR(8), ourfeedback.adddates,112) <= '20140117'
)
nested case used
CASE WHEN 1 then 'Yes'
CASE WHEN 0 then
CASE WHEN job_flow_state.no_fault_found = 1 THEN 'No fault found'
CASE WHEN job_flow_state.unable_to_repaired = 1 THEN 'Unable to repair'
CASE WHEN job_flow_state.Repair_Not_Requested = 1 THEN 'Repair Not Requested'
ELSE 'N/A'
END
END AS [Repaired]
I'd rewrite the nested case statement like so:
CASE WHEN 1 then 'Yes'
ELSE
CASE WHEN job_flow_state.no_fault_found = 1 THEN 'No fault found'
WHEN job_flow_state.unable_to_repaired = 1 THEN 'Unable to repair'
WHEN job_flow_state.Repair_Not_Requested = 1 THEN 'Repair Not Requested'
ELSE 'N/A'
END
END AS [Repaired]
You've got too many CASEs - you just need WHENs in the inner nesting. You also need to switch on a predicate - i.e. when 'what' is 1 or 0. And lastly, you should consider what happens if the inner case doesn't match.
i.e.:
CASE SomeColumn
WHEN 1 then 'Yes'
WHEN 0 then
CASE WHEN job_flow_state.no_fault_found = 1 THEN 'No fault found'
WHEN job_flow_state.unable_to_repaired = 1 THEN 'Unable to repair'
WHEN job_flow_state.Repair_Not_Requested = 1 THEN 'Repair Not Requested'
ELSE 'Oops'
END
ELSE 'N/A'
END AS [Repaired];
SqlFiddle here

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

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

Resources