SQL Server Remove unwanted rows from a CASE statement - sql-server

I do a SELECT with a CASE statement with this following:
SELECT DISTINCT
n.NiveauId, n.Description,
CASE WHEN n.NiveauId NOT IN (SELECT ccs.idNiveau WHERE ccs.centreCout = 60001) THEN 0 ELSE 1 END AS attribue
FROM pa.dbo.Niveau n
JOIN BDC.dbo.CentreCoutSecteur ccs ON n.NiveauId = ccs.idNiveau
Explication :
In case "NiveauId" is not present in the other table, the value of "attribue" is 0. Else, if it's present, the value is 1.
This works, but every rows that contains a 1 also shows the same row with a 0.
Exemple:
How would I change the SELECT query to remove the unwanted duplicate rows that contain 0?
Thanks in advance!

Try wrapping your select in a max (if you only want the rows with the highest value for attribue.
SELECT b.NiveauID, b.Description, MAX(b.attribue)
FROM
(SELECT DISTINCT
n.NiveauId, n.Description,
CASE WHEN n.NiveauId NOT IN (SELECT ccs.idNiveau WHERE ccs.centreCout = 60001) THEN 0 ELSE 1 END AS attribue
FROM pa.dbo.Niveau n
JOIN BDC.dbo.CentreCoutSecteur ccs ON n.NiveauId = ccs.idNiveau) b
Group By b.NiveauID, b.Description

Related

SQL Server Sum Returning Invalid Number

I am using LEFT JOIN three times in a query to display user information. For some reason, the second last LEFT JOIN affects the output of the SUM function.
I have tried removing the second last LEFT JOIN statement which returns the correct value. I don't see why it would change the value.
SELECT
[tbl_users].[id],
[username],
COUNT([tbl_password_resets].[id]) as passwordresets,
SUM(CASE WHEN [tbl_files].[user_id] = [tbl_users].[id] THEN 1 ELSE 0 END) as uploads,
COUNT([tbl_downloads].[id]) as downloads,
CAST(SUM(CASE WHEN [tbl_downloads].[liked] = 1 OR [tbl_downloads].[disliked] = 1 THEN 1 ELSE 0 END) AS FLOAT) / NULLIF(COUNT([tbl_downloads].[id]), 0) as ratio,
[ban]
FROM
[tbl_users]
LEFT JOIN
[tbl_password_resets] ON [tbl_users].[id] = [tbl_password_resets].[user_id]
LEFT JOIN
[tbl_downloads] ON [tbl_users].[id] = [tbl_downloads].[user_id]
LEFT JOIN
[tbl_files] ON [tbl_files].[user_id] = [tbl_users].[id]
GROUP BY
[tbl_users].[id], [tbl_users].[username], [tbl_users].[ban]
The result for uploads is 9 instead of 3.
If any joined table is duplicating rows, that affects the result of COUNTs and SUMs. Comment out the aggregates and the GROUP BY and the JOINs for testing and see what happens with the row count when you build the query adding the JOINs one-by-one. The more rows fall into one group, the more values will be COUNTed and SUMmed.

SUM vs EXIST in SqlServer

The intent is to return all 'Unprocessed' TransactionSets if they have NO PaymentUid and NO ProcessStatus.value('/CPI/#ProcessItem)[1]'... relations, and also pick up 'No-Matched-Payments' TransactionSets if they have ANY PaymentUid AND ANY ProcessStatus.value('/CPI/#ProcessItem)[1]'... relations.
The SUM function in the having seem clunky and don't allow SQL to quit when it encounters any or none. So it seems like it's inefficient, and at the very least quite clunky to read and deal with. Is there a way to write this with something like an EXIST ?
select ts.TransactionSetUid
from TransactionSet ts
join TransactionHeader eh on ts.TransactionSet = eh.TransactionSet
join TransactionPayment tp on eh.TransactionHeaderUid = tp.TransactionHeaderUid
left join ServicePayment sp on tp.TransactionPaymentUid = sp.TransactionPaymentUid
where TransactionStatus in ('Unprocessed', 'No-Matched-Payments')
group by ts.TransactionSet
having (TransactionStatus = 'Unprocessed'
and SUM( CASE WHEN sp.TransactionItem is null THEN 0 ELSE 1 END) = 0
and SUM( CASE WHEN tp.ProcessStatus.value('(/CPI/#ProcessItem)[1]', 'varchar(50)') IS NULL THEN 0 ELSE 1 END) = 0)
or (ts.RuleStatus = 'No-Matched-Payments'
and (SUM( CASE WHEN sp.TransactionItem is null THEN 0 ELSE 1 END) <> 0
or SUM( CASE WHEN tp.ProcessStatus.value('(/CPI/#ProcessItem)[1]', 'varchar(50)') IS NULL THEN 0 ELSE 1 END) <> 0))
UPDATE to answer questions. The relationships between the TransactionSet is one to many with the other tables. There could be many TransactionPayment records but the query is only concerned with ProcessStatus.value that has an xml node at (/CPI/#processItem)[1]. But with ServicePayment, any non-null TransactionItem will do.
As I understand it, the group by is only in there because of the SUM functions. The intent is to flag any TransactionSet that meets one of two conditions.
The first condition is:
the Transaction Status is 'Unprocessed'
and
there are no Process Status values
and
there are no Transaction Items.
The second condition is:
the Transaction Status is 'No-Matched-Payments'
and
there is at least one Process Status value
or
there is at least one Transaction Item.
So the query was set up to use SUM to count the number of times the left join on ServicePayment comes up NULL or when the XML value in TransactionPayment doesn't contain a '/CPI/#processItem'.
It seems to me that instead of using a SUM, the query could instead use an EXIST or some other mechanism to short circuit the test condition. The value of the SUM is not really important, It just needs to know if there is at least one or if there are none.
--
Thank you to everyone: I know i'm not a database expert by any means, and I've been programming in the seven C's (C,C++,C#,Java,etc.) for so long that I sometimes forget that SQL is not an imperative language, or more likely, I just don't think in declarative terms.
I think something like this should do the trick:
select ts.TransactionSetUid
from TransactionSet ts
where CASE WHEN EXISTS(SELECT * FROM TransactionHeader eh
join TransactionPayment tp on eh.TransactionHeaderUid = tp.TransactionHeaderUid
left join ServicePayment sp on tp.TransactionPaymentUid = sp.TransactionPaymentUid
where ts.TransactionSet = eh.TransactionSet and
(
sp.TransactionItem is not null or
tp.ProcessStatus.value('(/CPI/#ProcessItem)[1]', 'varchar(50)') IS not NULL
)
) THEN 1 ELSE 0 END =
CASE TransactionStatus
WHEN 'Unprocessed' THEN 0
WHEN 'No-Matched-Payments' THEN 1
END
That is, I've put the EXISTS check in to test for either condition and put it inside a CASE expression so that we don't have to write it out twice for which result we want (for Unprocessed and No-Matched-Payments).
I've also crafted the second CASE expression to return 0, 1 or NULL so that if the TransactionStatus is something else, it doesn't matter what result the EXISTS produces.
I hope I've followed the correct chains of 0/1, true/false, and/or, NULL/NOT NULL logic here - if it's not 100%, it's hopefully just tweaks to those options. I've also assumed I can shift all of the tables except TransactionSet into the EXISTS - it may be that TransactionHeader has to stay outside if that's where TransactionStatus is coming from.
If this isn't correct, you should probably add bare-bones tables and sample data to your question, alongside the expected results.
Yes, this might work... -- your query did not include a select distinct, but if this this produces duplicate TransactionSetUids, add the keyword distinct...
select [distinct] ts.TransactionSetUid from TransactionSet ts
join TransactionHeader th
on th.TransactionSet = ts.TransactionSet
join TransactionPayment tp
on tp.TransactionHeaderUid = th.TransactionHeaderUid
where not exists
( Select * from ServicePayment
Where TransactionPaymentUid = tp.TransactionPaymentUid
and tp.ProcessStatus.value(
'(/CPI/#ProcessItem)[1]', 'varchar(50)') IS NULL
and TransactionStatus = 'Unprocessed')
Or exists
( Select * from ServicePayment
Where TransactionPaymentUid = tp.TransactionPaymentUid
and ts.RuleStatus = 'No-Matched-Payments'
and tp.ProcessStatus.value(
'(/CPI/#ProcessItem)[1]', 'varchar(50)') IS not NULL
and ts.RuleStatus = 'No-Matched-Payments')

Second Order By overrides first in SQL Server

In the following code the purchase_price overrides the case order by statement.
SELECT
i.products_mpn, i.active,
i.distributor_code, i.sales_price
FROM
#duplicates d
LEFT JOIN
import i ON i.products_mpn = d.products_mpn
AND i.distributor_code = (SELECT TOP 1 distributor_code
FROM dbo.kting_ICECAT_import
WHERE products_mpn = d.products_mpn
ORDER BY
(SELECT
CASE
WHEN (i.distributor_code = 'x' or i.distributor_code = 'y') AND CAST(stock as int) > 0
THEN 2
WHEN CAST(stock as int) > 0
THEN 1
ELSE 0
END) DESC,
CAST(purchase_price AS decimal(28,12)))
Is there any way to get it to work with ordering by the case statement and then the purchase_price?
If you want the ordering of the CASE statement to be applied first followed by the ordering of the purchase_price, then your ORDER BY already has the terms in the right place. I don't know what SELECT is doing in your CASE expression, or even if your current query runs. Something like this may be what you had in mind:
ORDER BY CASE WHEN (i.distributor_code = 'x' or i.distributor_code = 'y') AND
CAST(stock as int) > 0 THEN 2
WHEN CAST(stock as int) > 0 THEN 1
ELSE 0
END DESC,
CAST(purchase_price AS DECIMAL(28,12))
The problem was in the ORDER BY SELECT. I had used columns from the left join "i.distributor_code" should just have been distributor_code

SQL Case Statement - If first 'when' returns null then complete the 2nd when

Just wondering if anyone can help me , I am running a case statement that references a different table. It needs to look up the make, model and year of a car as well as the position (FL,FR,BL,BR) and return the kit number.
Up to 4 entries can exist in the table for the same vehicle with the fitting position column specifying which kit number to be selected, in order to only return 1 result i believe i need to put this in the where section of the query, if i add it anywhere else more than 1 value is returned.
However 4 entries won't always exist for the vehicle. A kit can exist for FL & BL but not FR and BR. Because of me adding the position column into the where section 'null' is returned.Rather than it returning nothing i want it to return the next part of the case statement.
This is where the sql works because a kit is available for FL
SELECT CAST (CASE WHEN '002' != 'UNI' THEN T0.U_MPLFK ELSE 'NOKIT' END AS VARCHAR)
FROM
[#CSOL_MILFORD] T0 INNER JOIN [dbo].[#CSOL_VEHICLES] T1 ON T0.[U_VehicleRef] = T1.[U_VehicleRef]
WHERE
T1.U_Manufacturer = 'Ford'
AND
T1.U_Model = 'Galaxy'
AND
T0.U_MPLFK > 1
AND
T0.U_FittingPosition = 'FL'
However when it changes to
SELECT CAST (CASE WHEN '002' != 'UNI' THEN T0.U_MPLFK ELSE 'NOKIT' END AS VARCHAR)
FROM
[#CSOL_MILFORD] T0 INNER JOIN [dbo].[#CSOL_VEHICLES] T1 ON T0.[U_VehicleRef] = T1.[U_VehicleRef]
WHERE
T1.U_Manufacturer = 'Ford'
AND
T1.U_Model = 'Galaxy'
AND
T0.U_MPLFK > 1
AND
T0.U_FittingPosition = 'FR'
I get no value retuned, i want it to return 'NOKIT'
Many Thanks,
Roisin
A left join returns a row with null columns if its on condition fails. So you could move the conditions to the on part of a left join. Something like:
...
FROM #CSOL_VEHICLES T1
LEFT JOIN
#CSOL_MILFORD T0
ON T0.U_VehicleRef = T1.U_VehicleRef
AND T0.U_MPLFK > 1
AND T0.U_FittingPosition = 'FR'
WHERE T1.U_Manufacturer = 'Ford'
AND T1.U_Model = 'Galaxy'
Having the condition in the on clause instead of the where clause means you'll get a row with nulls instead of no row.

Optimize TSQL query with 3 tables

I need to get all the runs from the database, but need to mark if there is an error for this run.
3 Tables:
Runs: contains the runs)
Runfiles: contains the file ids that were processed during a run
Messages: contains errors, warnings, ...
Can this query be optimized any further?
SELECT TOP 1000 runid,
start,
end,
userid,
CASE
WHEN EXISTS(SELECT rf.fk_fileid
FROM runfiles rf
WHERE rf.fk_runid = r.runid
AND EXISTS(SELECT m.messageid
FROM messages m
WHERE m.fk_fileid =
rf.fk_fileid
AND m.fk_statusid = 4))
THEN 1
ELSE 0
END AS ContainsError
FROM runs r
ORDER BY start DESC
Please don't comment on the table names, they were translated for this question.
Thanks!
Try this:
SELECT TOP 1000
r.runid
,r.start
,r.[end]
,r.userid
,CASE WHEN m.messageid IS NOT NULL THEN 1 ELSE 0 END AS ContainsError
FROM runs r
LEFT JOIN runfiles rf
ON rf.fk_runid = r.runid
LEFT JOIN [messages] m
ON m.fk_fileid = rf.fk_fileid
AND m.fk_statusid = 4
ORDER BY r.start DESC
Anything in the select list is ran for each row in the result set. This means that the nested subquery in your CASE statement is being executed for each of those TOP 1000 rows.
Using left joins and a CASE statement to check if the primary key is null allow the entire statement to be evaluated as a set, which SQL Server is built to do. It should perform better this way.

Resources