SQL: Is there a clever way to show text data in pivoted columns without having inner queries or aggregate functions? - sql

I have a bunch of data (multiple rows for each unique reference) that needs to be in one row with multiple columns. Some of columns that need to be used have to be further split out as they hold more than one value. This has been done using an unpivot. I now have 7 columns from this 1 original column and it now needs to display statuses against the new 7 columns. I cannot however use a pivot as I need to see the various statuses in the 7 columns and not a min, max or a count.

You can perform this type of shift with a PIVOT function.
Static Pivot (See SQL Fiddle for Demo):
select *
from
(
select reference, jobtypesplit, status
from t1
) x
pivot
(
min(status)
for jobtypesplit in ([DDS], [MBN], [LPN], [WEN], [LLP], [OPE], [SSE])
) p
This can also be done dynamically (See SQL Fiddle)
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(jobtypesplit)
FROM t1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT reference, ' + #cols + ' from
(
select reference, jobtypesplit, status
from t1
) x
pivot
(
min(status)
for jobtypesplit in (' + #cols + ')
) p '
execute(#query)

Related

Convert Rows to Columns SQL

I am trying to convert rows to columns in SQL server. I am trying to convert the value's a product gets while being tested during quality. I have tried the pivot function but having trouble doing so as the same values do get repeated and it can not be easily sorted into rows. The table I am trying to pivot holds ~30K data row's so hoping to find a dynamic solution for this.
The maximum number of new columns is 30 but sometimes a product doesn't get tested as much so it can be less. The new column would be based off my inspection_unit_number field. Is this possible to achieve in SQL
Current data
What I hope to achieve
Current Attempt
SELECT BATCH , characteristic, [1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14],[15],[16],[17],[18],[19],[20],[21],[22],[23],[24],[25],[26],[27],[28],[29],[30]
from
(
select inspection_lot ,node_number ,characteristic ,inspector ,inspection_unit_number ,start_date ,measured_value ,original_value ,material_no ,batch
from stg.IQC_Tensile_TF
) d
pivot
(
max(measured_value)
for
INSPECTION_UNIT_NUMBER in ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14],[15],[16],[17],[18],[19],[20],[21],[22],[23],[24],[25],[26],[27],[28],[29],[30])
) piv;
You will have to go for a dynamic query, check if this will suit your needs.
I created a common table expression to be able to use distinct and then order by in the stuff function:
DECLARE #QUERY NVARCHAR(MAX)
DECLARE #Columns NVARCHAR(MAX)
WITH cte_unique_inspection_unit_number AS
(
SELECT DISTINCT QUOTENAME('TestResults' + CAST(inspection_unit_number AS VARCHAR)) TestResultsN,
inspection_unit_number
FROM IQC_Tensile_TF
)
SELECT #Columns = STUFF((SELECT ', ' + TestResultsN
FROM cte_unique_inspection_unit_number
ORDER BY inspection_unit_number
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,2,''),
#query = 'SELECT batch, node_number, characteristic, ' + #Columns + ' from
(
select batch,
node_number,
characteristic,
measured_value,
''TestResults'' + CAST(inspection_unit_number AS VARCHAR) TestResultsN
from IQC_Tensile_TF
) x
pivot
(
max(measured_value)
for TestResultsN in (' + #Columns + ')
) p '
EXEC(#query)
To view the execution in fiddle:
https://dbfiddle.uk/?rdbms=sqlserver_2014&fiddle=7898422e4422faacb25d7f3c2285f14a
If you find my answer useful, i would appreciate if you vote up and mark as accepted =D

Adding a WHERE statement refering another table in Dynamic SQL

I currently have the following script which is pivoting results from rows into columns. It works a Great, apart from two issues;
I have a flag in another table which I want to filter by (basically: WHERE Table.Stats=YES). I'd normally do this in a basic query with an inner join followed by that WHERE statement. In the query below, I already have a WHERE FileSeq=25, which works, but this criteria I need to get working is calling on a different table.
The query is returning a lot of uncessary NULL fields.
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
SELECT #cols = STUFF((SELECT ',' + QUOTENAME(col+CAST(rn AS varchar(6)))
FROM
(
SELECT row_number() over(partition by UID ORDER BY ClassCode) rn
FROM dbo.StudentClasses
) d
CROSS APPLY
(
SELECT 'ClassCode', 1
) c (col, so)
GROUP BY col, rn, so
ORDER BY rn, so
FOR XML PATH(''), TYPE
).VALUE('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT UID,' + #cols + '
FROM
(
SELECT UID, col+CAST(rn AS varchar(10)) col, VALUE
FROM
(
SELECT UID, classcode,
row_number() over(partition by UID ORDER BY classcode) rn
FROM StudentClasses WHERE FileSeq=25
) t
CROSS APPLY
(
SELECT ''classcode'', CAST(classcode AS varchar(6))
) c (col, VALUE)
) x
PIVOT
(
MAX(VALUE)
for col in (' + #cols + ')
) p '
EXECUTE(#query)
Any assistance appreciated

convert vertical sql result into horizontal output

I have a table, #t with 16 rows:
id int
description varchar(60)
balance decimal(6,2)
I need the description & balance data, and "select description, balance from #t order by id" will do the job. But ideally, I could do with showing the results horizontally rather than vertically.
Now I know I can build a new table with 16 columns and populate the balance for each such column using much dynamic sql, but, I'm also sure that this can be done a good deal more easily using pivot or something like that - though I dont really understand how.
Can someone please enlighted me?
Thanks
John
Assuming you are using SQL Server you can implement the PIVOT function to convert the rows of data into columns. The basic syntax will be:
select *
from
(
select description, balance
from yourtable
) d
pivot
(
sum(balance)
for description in ([desc1], desc2]) -- replace this with the names of your descriptions
) piv;
Of course if you have an unknown number of description values, then you will need to use dynamic SQL:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(description)
from yourtable
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT ' + #cols + '
from
(
select description, balance
from yourtable
) x
pivot
(
sum(balance)
for description in (' + #cols + ')
) p '
execute sp_executesql #query

Count Distinct Values FROM Column

I have Written a query which returns multiple column.Out of which One Column contains repetitive entries.
FAULT_SHORT_NAME
ATM DOWN DUE TO LINK PROBLEM
ATM DOWN DUE TO LINK PROBLEM
ALL CASSETTES FAULTED
ALL CASSETTES FAULTED
ATM IS MARK DOWN
ATM IS MARK DOWN
Now I want to Modify My query in Such a way that it will show me Value Count as
ATM DOWN DUE TO LINK PROBLEM ALL CASSETTES FAULTED ATM IS MARK DOWN
2 2 2
There Can be Different "FAULT_SHORT_NAME" Values so Cant Hard Code them.My Original Query is
Select * From ATMStatus S Left Join ATM A on S.ATM=A.Code
Left Join EventMsg E On S.Fault=E.Code
Where A.ATMStatus=0 AND S.TicketBooked <> 0
FAULT_SHORT_NAME is Column of Table "EventMsg"
It looks like you want a PIVOT since you want the values as columns instead of rows. there are two ways to do this either a Static or dynamic pivot.
Static Pivot, you hard-code the values of the columns:
SELECT *
FROM
(
Select *
From ATMStatus S
Left Join ATM A
on S.ATM=A.Code
Left Join EventMsg E
On S.Fault=E.Code
Where A.ATMStatus=0
AND S.TicketBooked <> 0
) x
PIVOT
(
count(*)
for FAULT_SHORT_NAME in ([ATM DOWN DUE TO LINK PROBLEM],
[ALL CASSETTES FAULTED], [ATM IS MARK DOWN])
) p
Dynamic Pivot, the columns are generated at run-time:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(FAULT_SHORT_NAME)
from EventMsg
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query
= 'SELECT ' + #cols + ' from
(
Select *
From ATMStatus S
Left Join ATM A
on S.ATM=A.Code
Left Join EventMsg E
On S.Fault=E.Code
Where A.ATMStatus=0
AND S.TicketBooked <> 0
) x
pivot
(
count(*)
for FAULT_SHORT_NAME in(' + #cols + ')
) p '
execute(#query)
Both will produce the same results. If you provide additional details about the tables and some sample data, then I could provide a more exact example.

SQL Pivot table returning NULL for non-existent child table values

I have a typical RDMS setup where records in a main table can have optional records in a related table via a M2M join. I'm trying to PIVOT this data but in cases where there is no relation I want to return a default value. The join I have below is returning NULL.
select *
from
(
SELECT s.Biz_Name, la.Name AS Association, ISNULL(i.Location, 'Default') as Location
FROM dbo.ShopAssociations sa
INNER JOIN dbo.LookupAssociations la
ON sa.AssociationID = la.AssociationID
RIGHT JOIN dbo.Basic_Shop_Info s
ON sa.ShopID = s.ShopID
INNER JOIN dbo.Images i
ON la.ImageID = i.ImageID
) DataTable
PIVOT
(
min(Location)
for association in
([OnCall],[OCGuy],[ASCLogo],[ASC_OtherSt],[ASE],[AASP],[AASP_PA],
[ASE_BlueSeal],[AAA],[AAA-B],[ASA],[ATRA],[ICAR],[CAA],[ACDelco],
[Cert],[ASC],[BBB],[Goodyear],[Limos],[RVs],[Bosch],[NARSA],
[DiscTire],[BigO],[Tires],[Firestone],[ASCCA],[JustTires],[ASE_Blue])
) PivotTable
The output looks like this:
BizName OnCall OCGuy ASCLogo ASC_OtherSt ASE ...
"Wonderful Biz" somevalue somevalue NULL somevalue NULL
What I am trying to achieve is if a child record doesn't exist in INNER JOIN from Basic_Shop_Info to ShopAssociations that we get "Default" instead of NULL. I've tried ISNULL(), Coalesce() and even a CASE statement, all with the same results.
Based on your comment it sounds like you found a solution. I am only answering this to provide a suggestion based on the fact you are pivoting so many columns and they are all hard-coded. You can use dynamic SQL for a PIVOT and your query would look something like this:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#colsPivot AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(Name)
from dbo.LookupAssociations
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select #colsPivot = STUFF((SELECT distinct ', IsNull(' + QUOTENAME(Name) +', ''Default'')'
from dbo.LookupAssociations
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT Bizname, ' + #colsPivot + ' from
(
SELECT s.Biz_Name, la.Name AS Association, ISNULL(i.Location, ''Default'') as Location
FROM dbo.ShopAssociations sa
INNER JOIN dbo.LookupAssociations la
ON sa.AssociationID = la.AssociationID
RIGHT JOIN dbo.Basic_Shop_Info s
ON sa.ShopID = s.ShopID
INNER JOIN dbo.Images i
ON la.ImageID = i.ImageID
) x
pivot
(
min(Location)
for association in (' + #cols + ')
) p
'
execute(#query)
The value #colsPivot is adding the IsNull() around each of you columns so you can put in place the Default value. But this should provide the same result as your original query where everything was hard-coded.
This will get the list of columns at run-time so then you do not have to hard-code anything and it will accept new values without having to change the query.
I got this:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(c.col+cast(rn as varchar(10)))
from
(
select row_number() over(partition by person_nbr
order by person_nbr,first_name, last_name, medication_name) rn
from TA_PIVOT
) d
cross apply
(
select 'diag' col, 1 sort
) c
group by col, rn, sort
order by rn, sort
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT person_nbr, first_name, last_name,medication_name,' + #cols + '
from
(
select person_nbr,first_name,last_name,medication_name,
col+cast(rn as varchar(10)) col,
value
from
(
-- when you perform an unpivot the datatypes have to be the same.
-- you might have to cast the datatypes in this query
select person_nbr,first_name,last_name, medication_name, cast(icd_code_id as varchar(500)) diag,
row_number() over(partition by person_nbr order by person_nbr, first_name, last_name,medication_name) rn
from ta_pivot
) src
unpivot
(
value
for col in (diag)
) unpiv
) d
pivot
(
max(value)
for col in (' + #cols + ')
) p '
execute(#query);