Turning rows into columns with a condition? SQL Server - sql

I have table1 with the following data for example:
PersonID Description StartDate EndDate
---------------------------------------------------------
ABC Do not call 2000-05-10 NULL
ABC Do not solicit 2005-10-15 NULL
XYZ Do not email 2010-12-20 NULL
FGH Do not contact 2020-10-11 2021-12-11
I am trying to turn each of the values in the Description column to be its own column and to show a "1" if the PersonID has this value in their row and as long as the EndDate is NULL for that Description, otherwise show "0" for example:
PersonID Do Not Call Do Not Solicit Do not email Do not contact
---------------------------------------------------------------------------------------------
ABC 1 1 0 0
XYZ 0 0 1 0
FGH 0 0 0 0
I've tried using PIVOT for example:
SELECT PersonID, [Do not call], [Do not call solicit], [Do not email], [Do not contact]
from table1 as SourceTable
PIVOT
(
COUNT(Description)
for Description IN ([Do not call], [Do not call solicit], [Do not email], [Do not contact])
) as PivotTable
But this gives me multiple rows of the same PersonID, whereas I just want one unique PersonID row, and then just value of 1 or 0 in the remaining columns of "Do not call", "Do not solicit", etc...
Also with my query, I am unable to check whether the row has an EndDate or not, because if it does have an EndDate value, then I want it to display 0 instead of 1.

Rather than a pivot I would choose to use conditional aggregation for this. Also, I would suggest not putting spaces in your column names as it just makes it harder to work with and provides no actual benefit.
select PersonID
, DoNotCall = MAX(case when Description = 'Do Not Call' then 1 end)
, DoNotSolicit = MAX(case when Description = 'Do Not Solicit' then 1 end)
, DoNotEmail = MAX(case when Description = 'Do not email' then 1 end)
, DoNotContact = MAX(case when Description = 'Do not contact' then 1 end)
from table1
group by PersonID
group by PersonID

Related

Assigning MIN(value) based on/grouped by another column

I'm using Microsoft SQL (SSMS):
UPC
Item
Department
1
a
0
2
b
23
3
c
33
4
a
0
I'm trying to write a query where if [Department] = 0, assign [UPC] as 'Department'
In this example, [Item]=a will have two [UPC] values with different [Department] values.
I'm trying to assign min([UPC] based on [Item] so I can see the following output:
UPC
Item
Department
1
a
1
4
a
1
This is what I have so far and I know this is wrong. Can anyone help me out with this query?
Thank you!
SELECT h.[UPC], h.[Item]
CASE WHEN h.[Department] = 0 THEN
CASE WHEN count(h.[UPC]) = 1 THEN h.[UPC]
ELSE f.[Min_UPC] END
ELSE h.[Department] END AS 'Department'
FROM [table]h
LEFT JOIN
(SELECT [Item], MIN([UPC]) AS 'Min_UPC' FROM [table]
GROUP BY [Item])f on h.[Item] = f.[Item]
GROUP BY h.[UPC], h.[Item], f.[Min_UPC], h.[Department]
You can use the windowed version of min() to get the minimum upc for each item and assign it when department equals 0 using a CASE expression.
SELECT upc,
item,
CASE
WHEN department = 0 THEN
min(upc) OVER (PARTITION BY item)
ELSE
department
END AS department
FROM elbat;

SELECTING from same field in 2 column

I want to create a report that appears from the same field in two columns
example :
receipt id infotype information
-----------------------------------------------
1 phone number 123
1 comment no comment
2 phone number 12346
2 any comment price is high
Result :
receipt id phone number comment
----------------------------------------------
1 123 no comment
2 12346 price is high
I use this code but the result is not same I need:
SELECT
RETAILTRANSACTIONINFOCODETRANS.TRANSDATE AS "Date" ,
RETAILTRANSACTIONTABLE.RECEIPTID AS "Receipt number" ,
RETAILTRANSACTIONSALESTRANS.staff As "Staff" ,
RETAILTRANSACTIONTABLE.NETAMOUNT AS "Total" ,
RETAILTRANSACTIONINFOCODETRANS.INFOCODEID AS " Type " ,
RETAILTRANSACTIONINFOCODETRANS.INFORMATION,
MAX(CASE WHEN RETAILTRANSACTIONINFOCODETRANS.INFOCODEID = 'Phone Numb' THEN information END) AS phonenumber,
MAX(CASE WHEN RETAILTRANSACTIONINFOCODETRANS.INFOCODEID LIKE '%Any Commen%' THEN information END) AS comment
FROM
RETAILTRANSACTIONTABLE
INNER JOIN
RETAILTRANSACTIONINFOCODETRANS ON RETAILTRANSACTIONINFOCODETRANS.TRANSACTIONID = RETAILTRANSACTIONTABLE.TRANSACTIONID
INNER JOIN
RETAILTRANSACTIONSALESTRANS ON RETAILTRANSACTIONSALESTRANS.TRANSACTIONID = RETAILTRANSACTIONINFOCODETRANS.TRANSACTIONID
GROUP BY
GROUPING SETS ((RETAILTRANSACTIONINFOCODETRANS.TRANSDATE,
RETAILTRANSACTIONTABLE.RECEIPTID,
RETAILTRANSACTIONSALESTRANS.staff,
RETAILTRANSACTIONTABLE.NETAMOUNT,
RETAILTRANSACTIONINFOCODETRANS.INFORMATION,
RETAILTRANSACTIONINFOCODETRANS.INFOCODEID ),())
You can use conditional aggregation
select receiptid,
max(case when infotype='phone number' then information end) as phonenumber,
max(case when infotype like '%comment%' then information end) as comment
from tablename
group by receiptid
select * from
(
SELECT [receipt id]
,[infotype]
,[information]
FROM [test].[dbo].[report]) as x
pivot
(
max([information])
for [infotype] in([phone],[comment]) )as pvt

Transpose rows from split_string into columns

I'm stuck trying to transpose a set of rows into a table. In my stored procedure, I take a delimited string as input, and need to transpose it.
SELECT *
FROM string_split('123,4,1,0,0,5|324,2,0,0,0,4','|')
CROSS APPLY string_split(value,',')
From which I receive:
value value
123,4,1,0,0,5 123
123,4,1,0,0,5 4
123,4,1,0,0,5 1
123,4,1,0,0,5 0
123,4,1,0,0,5 0
123,4,1,0,0,5 5
324,2,0,0,0,4 324
324,2,0,0,0,4 2
324,2,0,0,0,4 0
324,2,0,0,0,4 0
324,2,0,0,0,4 0
324,2,0,0,0,4 4
The values delimited by | are client details. And within each client, there are six attributes, delimited by ,. I would like an output table of:
ClientId ClientTypeId AttrA AttrB AttrC AttrD
------------------------------------------------
123 4 0 0 0 5
324 2 0 0 0 4
What's the best way to go about this? I've been looking at PIVOT but can't make it work because it seems like I need row numbers, at least.
This answer assumes that row number function will "follow the order" of the string. If it does not you will need to write your own split that includes a row number in the resulting table. (This is asked on the official documentation page but there is no official answer given).
SELECT
MAX(CASE WHEN col = 1 THEN item ELSE null END) as ClientId,
MAX(CASE WHEN col = 2 THEN item ELSE null END) as ClientTypeId,
MAX(CASE WHEN col = 3 THEN item ELSE null END) as AttrA,
MAX(CASE WHEN col = 4 THEN item ELSE null END) as AttrB,
MAX(CASE WHEN col = 5 THEN item ELSE null END) as AttrC,
MAX(CASE WHEN col = 6 THEN item ELSE null END) as AttrD
FROM (
SELECT A.value as org, B.value as item,
ROW_NUMBER() OVER (partition by A.value) as col
FROM string_split('123,4,1,0,0,5|324,2,0,0,0,4','|') as A
CROSS APPLY string_split(A.value,',') as B
) X
GROUP BY org
You might get a message about nulls in aggregate function ignored. (I always forget which platforms care and which don't.) If you do you can replace the null with 0.
Note, this is not as fast and using a CTE to find the 5 commas in the string with CHARINDEX and then using SUBSTRING to extract the values. But I'm to lazy to write up that solution which I would need to test to get all the off by 1 issues right. Still, I suggest you do it that way if you have a big data set.
I know you already got it pretty much answered, but here you can find a PIVOT solution
select [ClientID],[ClientTypeId],[AttrA],[AttrB],[AttrC],[AttrD]
FROM
(
select case when ColumnRow = 1 then 'ClientID'
when ColumnRow = 2 then 'ClientTypeId'
when ColumnRow = 3 then 'AttrA'
when ColumnRow = 4 then 'AttrB'
when ColumnRow = 5 then 'AttrC'
when ColumnRow = 6 then 'AttrD' else null end as
ColumnRow,t.value,ColumnID from (
select ColumnID,z.value as stringsplit,b.value, cast(Row_number()
over(partition by z.value order by z.value) as
varchar(50)) as ColumnRow from (SELECT cast(Row_number() over(order by
a.value) as
varchar(50)) as ColumnID,
a.value
FROM string_split('123,4,1,0,0,5|324,2,0,0,0,4','|') a
)z
CROSS APPLY string_split(value,',') b
)t
) AS SOURCETABLE
PIVOT
(
MAX(value)
FOR ColumnRow IN ([ClientID],[ClientTypeId],[AttrA],[AttrB],[AttrC],
[AttrD])
)
AS
PivotTable

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"';

Multiple nested queries to count total of each category

I would like to ask for any suggestions on how to do the following on SQL Server
The required output columns would be
EmpName | CompleteCalls | IncompleteCalls
which basically counts each Employee's number of Complete Calls and Incomplete Calls
the current table structure is like this
EmpName | Flag
the flag in bit data type that represents either a Complete call or Incomplete call. An Employee's name can repeat in different rows for each type of Call.
For example, an employee can have both flags of Complete and Incomplete Calls in 2 rows.
Now how do I do this in SQL Server? So far I have tried the following:
select EmpName,
(select count(*)
from call_log
where flag = 1
and EmpName in (select EmpName
from call_log
where flag = 1) ) AS [CompleteCalls]
FROM call_log
but as you may notice, the query result is not accurate since each Employee will get the total count of complete calls currently on the table.
TIA
Try this:
SELECT EmpName,
SUM(CASE WHEN Flag = 1 THEN 1 ElSE 0 END) AS COMPLETECALLS,
SUM(CASE WHEN Flag = 0 THEN 1 ElSE 0 END) AS INCOMPLETECALLS
FROM call_log
GROUP BY EmpName
It will SUM 1 for each row in the table that has flag = 1 or flag = 0 respectively.
select SUM(CASE WHEN Complete=1 THEN 1 ELSE 0 END) AS NumComplete
, SUM(CASE WHEN Complete=0 THEN 1 ELSE 0 END) AS NumIncomplete
, EmpName
FROM call_log
GROUP BY EmpName