compare all data in each column in mssql - sql

I have a table as follows
OrderId Carrier Truck Trailer
10001 ABC TruckA TrailerA
10001 ABC TruckA TrailerA
10001 ABC TruckB TrailerA
10001 ABC TruckC TrailerB
10001 ABC TruckC TrailerD
The Output of my query should be single row
OrderId Carrier Truck Trailer
10001 ABC NULL NULL
The logic have to be applied as if the each column contains the same value then that value have to be in that particular column otherwise it would be NULL
I tried the below queries, but not getting correct output
SELECT
s.OrderId
,LEAD(s.OrderId,1) OVER (ORDER BY t.Code) AS PreviousOrderId
,c.LegalName AS CarrierName
,LEAD(c.LegalName,1) OVER (ORDER BY t.Code) AS PreviousCarrierName
,t.TruckLicensePlate
,LEAD(t.TruckLicensePlate,1) OVER (ORDER BY t.Code) AS PreviousTruckLicensePlate
,t.TrailerLicensePlate
,LEAD(t.TrailerLicensePlate,1) OVER (ORDER BY t.Code) AS PreviousTrailerLicensePlate
INTO #tempResult
FROM
OrderDetails
SELECT
CASE WHEN t.OrderId = t.PreviousOrderId
THEN t.OrderId ELSE NULL END AS OrderId
,CASE WHEN t.CarrierName = t.PreviousCarrierName
THEN t.CarrierName ELSE NULL END AS CarrierName
,CASE WHEN t.TruckLicensePlate = t.PreviousTruckLicensePlate
THEN t.TruckLicensePlate ELSE NULL END AS TruckLicensePlate
,CASE WHEN t.TrailerLicensePlate = t.PreviousTrailerLicensePlate
THEN t.TrailerLicensePlate ELSE NULL END AS TrailerLicensePlate
from #tempResult t
order by orderid desc

The logic have to be applied as if the each column contains the same value then that value have to be in that particular column otherwise it would be NULL
Use aggregation and conditional logic. I find that comparing min() and max() is a straight-forward approach:
select
orderid,
case when min(carrier) = max(carrier) then min(carrier) end carrier,
case when min(truck) = max(truck) then min(carrier) end truck,
case when min(trailer) = max(trailer) then min(carrier) end trailer
from orderdetails
group by orderid

Seems like you could just use a CASE with COUNT and MAX:
SELECT CASE COUNT(DISTINCT OrderID) WHEN 1 THEN MAX(OrderID) END AS OrderID,
CASE COUNT(DISTINCT Carrier) WHEN 1 THEN MAX(Carrier) END AS Carrier,
CASE COUNT(DISTINCT Truck) WHEN 1 THEN MAX(Truck) END AS Truck,
CASE COUNT(DISTINCT Trailer) WHEN 1 THEN MAX(Trailer) END AS Trailer
FROM dbo.YourTable;

Related

Aggregating values in SQL

Im trying to aggregate output values of a customer's bankruptcy in terms of yes (Y), no (N) or no data (N/D) using a window function in a subquery below. For example, if there arise edge cases when in one record the Customer is classified as not bankrupt (N) but in another record on the same CDate it is also classified as no data (N/D), I should get a final aggregated output value as N/D, but it gives me N instead, because of what I've done here by partitioning the customer records over IsBankrupt ascending (asc). The logic behind it which is supposed to be implemented:
Y and Y = Y;
Y and N = Y;
N and N = N;
Y and N/D = Y;
N and N/D = N/D
with sample as (
select date('2020-12-32') as CDate, 123 as CustomerID, 'N/D' as IsBankrupt
union all
select date('2020-12-32') as CDate, 123 as CustomerID, 'N' as IsBankrupt)
select CDate, CustomerID, IsBankrupt, case when CustomerID = 123 then 'N/D' end as ExpectedResult
from
(
select CDate, CustomerID, IsBankrupt,
row_number() over (partition by CustomerID, CDate order by IsBankrupt asc) as flag
from sample
) from subsample
where flag = 1
output:
CDate
CustomerID
IsBankrupt
ExpectedOutput
2020-12-31
123
N
N/D
All the other cases of the previously mentioned logic work. So Question is - how could i update my row_number() over partition by clause so that the logic doesnt break down?
I would suggest aggregation:
select cdate, customerid,
(case when sum(case when IsBankrupt = 'Y' then 1 else 0 end) > 0
then 'Y'
when sum(case when IsBankrupt = 'N/D' then 1 else 0 end) > 0
then 'N/D'
else 'N'
end) as new_flag
from t
group by cdate, customerid;
If you don't like the nested case expressions, you can actually do this based on the ordering of the values:
select cdate, customerid,
max(IsBankrupt) as new_flag
from t
group by cdate, customerid;

Oracle SQL -- Pick Max Value from RN Function but update all fields with that value

My query is as follows
SELECT HEADER_TABLE.SEGMENT1,
LINES_TABLE.LINE_NUM,
CASE
WHEN ( HEADER_TABLE.REVISION_NUM = '0'
AND HEADER_TABLE.PRINT_COUNT = '0')
THEN
'Unavailable'
ELSE
NVL (ACK_TABLE.ACK_TYPE, 'Absent')
END
AS X_ACK_TYPE,
ACK_TABLE.GXS_DATE
FROM HEADER_TABLE,
LINES_TABLE,
(SELECT po_number,
po_line_number,
gxs_date,
po_ack_filename,
ack_type
FROM (SELECT po_number,
po_line_number,
gxs_date,
po_ack_filename,
ack_type,
ROW_NUMBER ()
OVER (PARTITION BY po_number ORDER BY gxs_date DESC)
rn
FROM xxcmst_po_ack_from_gxs_stg)
WHERE rn = 1) ACK_TABLE,
(SELECT PO_NUMBER FROM XXCMST.XXCMST_ACTION_TABLE_ACKNOWLEDGEMENT) ACTION_TABLE
WHERE HEADER_TABLE.PO_HEADER_ID = LINES_TABLE.PO_HEADER_ID
AND HEADER_TABLE.SEGMENT1 = ACK_TABLE.PO_NUMBER(+)
AND HEADER_TABLE.SEGMENT1 = ACTION_TABLE.PO_NUMBER(+)
AND LINES_TABLE.LINE_NUM = ACK_TABLE.PO_LINE_NUMBER(+)
AND HEADER_TABLE.SEGMENT1 = '100';
This is giving me 6 records with 1 GXS_DATE and X_ACK_TYPE = 'Absent'. The RN function is needed here to pull 1 record only from the subquery but the requirement is to have all the 6 records have the same date and ACK_TYPE which is not happening. How can I achieve this? Please refer to the below screenshot and I need X_ACK_TYPE = AK for all the 6 LINE_NUMs and GXS_DATE = 3/6/2020 for all these 6 records.
My current data screenshot here
Instead of
ACK_TABLE.GXS_DATE
in SELECT clause use the LAG function as follows:
CASE WHEN ACK_TABLE.GXS_DATE IS NOT NULL
THEN ACK_TABLE.GXS_DATE
ELSE LAG(ACK_TABLE.GXS_DATE IGNORE NULLS)
OVER (PARTITION BY HEADER_TABLE.SEGMENT1 ORDER BY LINES_TABLE.LINE_NUM )
END AS GXS_DATE
or If there will be always one value of ACK_TABLE.GXS_DATE exists per HEADER_TABLE.SEGMENT1 then you can simply write it as
MIN(ACK_TABLE.GXS_DATE)
OVER (PARTITION BY HEADER_TABLE.SEGMENT1) AS GXS_DATE
-- Update --
for ACK_TYPE, You need to apply the same logic in ELSE portion of your CASE statement from the original query as follows:
Replace this:
ELSE
NVL (ACK_TABLE.ACK_TYPE, 'Absent')
END
With this:
ELSE
NVL (MIN(ACK_TABLE.ACK_TYPE)
OVER (PARTITION BY HEADER_TABLE.SEGMENT1), 'Absent')
END

How to filter rows based on group values in SQL?

I have a table with the following format
serialnumber,test,result
-------------------------
ABC 1 "TOO HIGH"
ABC 2 "PASS"
ABC 3 "TOO LOW"
DEF 1 "PASS"
DEF 2 "PASS"
DEF 3 "PASS"
I need to do two operations:
1) for each serial number that has all pass records, I need to roll it up into a single record
2) for every serial that contains a "TOO HIGH" or "TOO LOW" record, I need to exclude all "PASS" records for that serial number
How would I go about doing this in teradata 15, preferably in a single statement?
SELECT *
FROM tab
QUALIFY
-- #1, only PASS
( SUM(CASE WHEN result <> 'PASS' THEN 1 ELSE 0 end)
OVER (PARTITION BY serialnumber) = 0
AND ROW_NUMBER()
OVER (PARTITION BY serialnumber
ORDER BY test) = 1
)
OR
-- #2
( SUM(CASE WHEN result <> 'PASS' THEN 1 ELSE 0 end)
OVER (PARTITION BY serialnumber) > 0
AND result_ <> 'PASS'
)
Consider a union query combining both conditions, using an aggregate query for #1 and a inner join query with derived tables for #2. Hopefully, Teradata's dialect supports the syntax:
SELECT TableName.SerialNumber,
Min(TableName.Test) As Test,
Min(TableName.Result) As Result
FROM SerialNumber
GROUP BY SerialNumber
HAVING Sum(CASE WHEN TableName.Result='"PASS"' THEN 1 ELSE 0 END) = Count(*)
UNION
SELECT TableName.SerialNumber,
TableName.Test,
TableName.Result
FROM SerialNumber
INNER JOIN
(SELECT SerialNumber FROM SerialNumber
WHERE TableName.Result = '"TOO HIGH"') AS toohighSub
INNER JOIN
(SELECT SerialNumber FROM SerialNumber
WHERE TableName.Result = '"TOO LOW"') AS toolowSub
ON toolowSub.SerialNumber = toohighSub.SerialNumber
ON TableName.SerialNumber = toolowSub.SerialNumber
WHERE TableName.Result <> '"PASS"';

Return true from query when all values are 0

I have this
id number stock
1 555 1
2 555 0
3 444 0
I want to return true when all the values are 0 on the same number.
Number 555 is false, because there is 1 on stock. Number 3 is true because there are all stock values 0.
How can i do this?
You can do this:
select
number,
case
when min(stock) = 0 and max(stock) = 0 then 'true'
else 'false'
end as TrueOrFalse
from TheTable
group by number
The query will return all distinct values in the number column, along with the corresponding true/false status.
Here's a live demo: http://www.sqlfiddle.com/#!9/61c82/1
SELECT number,
CASE WHEN COUNT(CASE WHEN stock <> 0 THEN 1 END) = 0
THEN 'true' ELSE 'false' END
FROM dbo.TableName
GROUP BY number
Demo
If you want to display indicator for all the rows then try this
WITH cte
AS (SELECT Count(CASE WHEN stock = 0 THEN 1 END)OVER(partition BY number) f_count,
Count(1)OVER(partition BY number) AS t_count,*
FROM Yourtable)
SELECT id,
number,
stock,
Indicator=CASE WHEN f_count <> t_count THEN 'False' ELSE 'True' END
FROM cte
SQL FIDDLE DEMO
You can use this
select number,stock,
case when sum(stock)=0 then 'true' else 'false' end AS Staus
from #temp group by number,stock

SQL CASE WHEN to narrow results, change field

I've read dozens of answers regarding CASE and am not sure that is what i need to be using here, it seems like it should work but its not:
Data:
OrderNum OrderLine PartNum
200011 1 ABC-1
200011 2 DEF-1
200012 1 XYZ-1
What I would like to return:
OrderNum Item#
200011 MIXED
200012 XYZ-1
What I am returning instead:
OrderNum Item#
200011 ABC-1
200011 MIXED
200012 XYZ-1
My query:
SELECT OrderHed.OrderNum,
(CASE WHEN ShipDtl.OrderLine > '1' then 'MIXED' else ShipDtl.PartNum end) as [Item#]
FROM dbo.OrderHed, dbo.ShipDtl
WHERE ShipDtl.Company = OrderHed.Company
AND ShipDtl.OrderNum = OrderHed.OrderNum
GROUP BY OrderHed.OrderNum, ShipDtl.OrderLine, ShipDtl.Part
Try with grouping like
SELECT OrderHed.OrderNum,
(CASE WHEN SUM(ShipDtl.OrderLine) > 1 then 'MIXED' else MAX(ShipDtl.PartNum) end) as [Item#]
FROM dbo.OrderHed, dbo.ShipDtl
WHERE ShipDtl.Company = OrderHed.Company
AND ShipDtl.OrderNum = OrderHed.OrderNum
GROUP BY OrderHed.OrderNum
SQLFiddle Demo : http://sqlfiddle.com/#!3/209d8/1
You didn't write which database engine you use, but if it is SQL 2005 and above, I'd think using the window function for COUNT will make things easier as you then do not need to group.
SELECT DISTINCT
OrderHed.OrderNum ,
CASE
WHEN COUNT(ShipDtl.OrderLine) OVER (PARTITION BY ShipDtl.OrderNum) > 1 THEN 'MIXED'
ELSE PartNum
END AS [Item#]
FROM dbo.OrderHed ,
dbo.ShipDtl
WHERE ShipDtl.Company = OrderHed.Company
AND ShipDtl.OrderNum = OrderHed.OrderNum
You'll need to DISTINCT though, because it'll select a row per line, but each order with multiple lines will be MIXED, so you can easily distinct.
This will simply select the OrderNum and if multiple orderlines exists per ordernum Count(xxx) OVER (partition by yyy) it'll select 'MIXED', otherwise the partnum.
And then distinct the result.
There is no compulsion to use CASE (is there?).
In your example, CASE is being used to perform logical OR. There are other ways of performing logical OR in SQL e.g. UNION:
WITH T
AS
(
SELECT OrderHed.OrderNum, ShipDtl.PartNum, ShipDtl.OrderLine
FROM dbo.OrderHed, dbo.ShipDtl
WHERE ShipDtl.Company = OrderHed.Company
AND ShipDtl.OrderNum = OrderHed.OrderNum
)
SELECT OrderNum, PartNum
FROM T
WHERE OrderLine = 1
AND OrderNum NOT IN (
SELECT OrderNum
FROM T T2
WHERE OrderLine > 1
)
UNION
SELECT OrderNum, 'MIXED' AS PartNum
FROM T
WHERE OrderLine > 1;