Related
Im using two query's, 1st separated one column to two columns and inserted one table and second query (PIVOT) fetching based on inserted table,
1st Query,
SELECT A.MDDID, A.DeviceNumber,
Split.a.value('.', 'VARCHAR(100)') AS MetReading
FROM (
SELECT MDDID,DeviceNumber,
CAST ('<M>' + REPLACE(Httpstring, ':', '</M><M>') + '</M>' AS XML) AS MetReading
FROM [IOTDBV1].[dbo].[MDASDatas] E
Where E.MDDID = 49101
) AS A CROSS APPLY MetReading.nodes ('/M') AS Split(a);
2nd Query
SELECT * FROM
(
Select ID,MDDID,DeviceNumber,ReceivedDate
, ROW_NUMBER() OVER(PARTITION BY ID ORDER BY (SELECT 1)) AS ID2
, SPLT.MR.value('.','VARCHAR(MAX)') AS LIST FROM (
Select ID,MDDID,DeviceNumber,ReceivedDate
, CAST( '<M>'+REPLACE(MeterReading,',','</M><M>')+'</M>' AS XML) AS XML_MR
From [dbo].[PARSEMDASDatas] E
Where E.MeterReading is Not Null
)E
CROSS APPLY E.XML_MR.nodes('/M') AS SPLT(MR)
)A
PIVOT
(
MAX(LIST) FOR ID2 IN ([1],[2],[3],[4],[5],[6],[7],[8])
)PV
I want 2nd query run based on first query no need to require table.
any help would be appreciated.
Your question is not very clear... And it is a very good example, why you always should add a MCVE, including DDL, sample data, own attempts, wrong output and expected output. This time I do this for you, please try to prepare such a MCVE the next time yourself...
If I get this correctly, your source table includes a CSV column with up to 8 (max?) values. This might be solved much easier, no need to break this up in two queries, no need for an intermediate table and not even for PIVOT.
--create a mockup-table to simulate your situation (slightly shortened for brevity)
DECLARE #YourTable TABLE(ID INT,MDDID INT, DeviceNumber VARCHAR(100),MetReading VARCHAR(2000));
INSERT INTO #YourTable VALUES
(2,49101,'NKLDEVELOPMENT02','DCPL,981115,247484,9409') --the character code and some numbers
,(3,49101,'NKLDEVELOPMENT02','SPPL,,,,,,,,') --eigth empty commas
,(4,49101,'NKLDEVELOPMENT02','BLAH,,,999,,'); --A value somewhere in the middle
--The cte will return the table as is. The only difference is a cast to XML (as you did it too)
WITH Splitted AS
(
SELECT ID
,MDDID
,DeviceNumber
,CAST('<x>' + REPLACE(MetReading,',','</x><x>') + '</x>' AS XML) AS Casted
FROM #YourTable t
)
SELECT s.ID
,s.MDDID
,s.DeviceNumber
,s.Casted.value('/x[1]','varchar(100)') AS [1]
,s.Casted.value('/x[2]','varchar(100)') AS [2]
,s.Casted.value('/x[3]','varchar(100)') AS [3]
,s.Casted.value('/x[4]','varchar(100)') AS [4]
,s.Casted.value('/x[5]','varchar(100)') AS [5]
,s.Casted.value('/x[6]','varchar(100)') AS [6]
,s.Casted.value('/x[7]','varchar(100)') AS [7]
,s.Casted.value('/x[8]','varchar(100)') AS [8]
FROM Splitted s;
the result
ID MDDID DeviceNumber 1 2 3 4 5 6 7 8
2 49101 NKLDEVELOPMENT02 DCPL 981115 247484 9409 NULL NULL NULL NULL
3 49101 NKLDEVELOPMENT02 SPPL
4 49101 NKLDEVELOPMENT02 BLAH 999 NULL NULL
The idea in short:
Each CSV is tranformed to a XML similar to this:
<x>DCPL</x>
<x>981115</x>
<x>247484</x>
<x>9409</x>
Using a position predicate in the XPath, we can call the first, the second, the third <x> easily.
CTE: WITH common_table_expression is answer
you can prepare some data in first query and user in second
WITH cte_table AS
(
SELECT *
FROM sys.objects
)
SELECT *
FROM cte_table
where name like 'PK%'
This is static pivot query:
SELECT
*
FROM
(SELECT
PI.PRODID||' '||' ('||PI.PRODNAME||' - '||PI.PACKSIZE||')' PNAME,I.SIA_ID, IP.SALES_QTY
FROM
DIS_INVOICE_INFO I, DIS_INVOICE_PRODUCT IP, MKT_PROD_INFO PI
WHERE
I.SLNO = IP.SLNO
AND PI.PRODID = IP.PRODID) s
PIVOT
(SUM(SALES_QTY)
FOR SIA_ID IN (10080, 10084, 10082)
) pvt
But I want a dynamic query.
Like:
......
PIVOT
(SUM(SALES_QTY)
FOR SIA_ID IN (SELECT DISTINCT SIA_ID FROM DIS_INVOICE_INFO)
) pvt
You didn't mention about your DBMS, I don't know for the other DBMS systems but for Oracle you may make it dynamic provided you're producing XML results as in the following case :
SELECT
*
FROM
(SELECT PI.PRODID||' '||' ('||PI.PRODNAME||' - '||PI.PACKSIZE||')' PNAME,
I.SIA_ID, IP.SALES_QTY
FROM DIS_INVOICE_INFO I
INNER JOIN DIS_INVOICE_PRODUCT IP
ON (I.SLNO = IP.SLNO)
INNER JOIN MKT_PROD_INFO PI
ON (PI.PRODID = IP.PRODID)) s
PIVOT XML
(SUM(SALES_QTY)
FOR SIA_ID IN (SELECT DISTINCT SIA_ID FROM DIS_INVOICE_INFO)
) pvt;
By using the following DDL statements ;
create table DIS_INVOICE_INFO(SIA_ID int, SLNO int);
create table DIS_INVOICE_PRODUCT(PRODID int, SALES_QTY int, SLNO int);
create table MKT_PROD_INFO(PRODID int,PRODNAME varchar(90),PACKSIZE int);
insert all
into DIS_INVOICE_INFO values(10080,1);
into DIS_INVOICE_PRODUCT values(1,100,1);
into MKT_PROD_INFO values(1,'Milk',200);
select * from dual;
You get the following results for the above SQL-Select statement :
PNAME SIA_ID_XML
------------ ----------
(Milk - 200) <CLOB>
where <CLOB> is
<PivotSet><item><column name = "SIA_ID">10080</column>
<column name = "SUM(SALES_QTY)">100</column></item></PivotSet>
P.s. You cannot use PIVOT dynamically without XML option, I mean need to list all arguments explicitly within IN clause as in your case.
I have the following table structure (partially excluded for clarity of question):
The table sometimes receives two lowFareRQ and lowFareRS that is considered to be only one booking under BookingNumber. The booking is then processed into a ticket where each booking number always have the same TicketRQ and TicketRS if the user proceeded with the booking. TicketRS contains 3rd party reference number.
I now want to display all the active bookings to the user in order to allow the user to cancel a booking if he wanted to.
So I would naturally want to retrieve the each booking number with active status as well as the TicketRS xml data in order to get the 3rd party reference number.
Here is the SQL query I started with:
SELECT TOP 100
[BookingNumber]
,[Status]
,[TicketRS]
FROM [VTResDB].[dbo].[LowFareRS]
GROUP BY [BookingNumber],[Status],[TicketRS]
ORDER BY [recID] desc
Now with MS SQL Management Studio you have to add the field [TicketRS] to 'GROUP BY' if you want to have it in the 'SELECT' field list... but you cannot have a XML field in the 'GROUP BY' list.
The XML data type cannot be compared or sorted, except when using the IS NULL operator.
I know that if I change the table structure this problem can be solved without any issue but I want to avoid changing the table structure because I am just completing the software and do not want to rewrite existing code.
Is there a way to return a XML field when using GROUP BY clause in MS SQL Management Studio?
Uhm, this seems dirty... If your XMLs are identically within the group, you might try something like this:
DECLARE #tbl TABLE(ID INT IDENTITY,Col1 VARCHAR(100),SomeValue INT,SomeXML XML);
INSERT INTO #tbl(col1,SomeValue,SomeXML) VALUES
('testA',1,'<root><a>testA</a></root>')
,('testA',2,'<root><a>testA</a></root>')
,('testB',3,'<root><a>testB</a></root>')
,('testB',4,'<root><a>testB</a></root>');
WITH GroupedSource AS
(
SELECT SUM(SomeValue) AS SumColumn
,CAST(SomeXml AS NVARCHAR(MAX)) AS XmlColumn
FROM #tbl AS tbl
GROUP BY Col1,CAST(SomeXml AS NVARCHAR(MAX))
)
SELECT SumColumn
,CAST(XmlColumn AS XML) AS ReCasted
FROM GroupedSource
Another approach was this
WITH GroupedSource AS
(
SELECT SUM(SomeValue) AS SumColumn
,MIN(ID) AS FirstID
FROM #tbl AS tbl
GROUP BY Col1
)
SELECT SumColumn
,(SELECT SomeXML FROM #tbl WHERE ID=FirstID) AS ReTaken
FROM GroupedSource
Cast it to nvarchar(max) and back
with t(xc,val) as (
select xc=cast(N'<x><y>txt</y></x>' as xml), val = 5
union all
select xc=cast(N'<x><y>txt</y></x>' as xml), val = 6
)
select xc = cast(xc as XML), val
from (
select xc = cast(xc as nvarchar(max)), val = sum(val)
from t
group by cast(xc as nvarchar(max))
) tt
;
I am trying to populate a Gridview to have checkboxes enabled per student, but depending to certain values from this query:
#SelectedDate is provided via a TextBox as a date only
SELECT v1.StudentID,
v1.StudentPreferredName + ' ' + v1.StudentFamilyName AS StudentName,
bcs.CheckStatusName,
rce.DateSubmitted,
rcp.RollCallPeriod
FROM tblBoardingRollCallEntries AS rce
INNER JOIN vwBoardingTenants AS v1
ON v1.StudentID = rce.StudentID
AND v1.[Year] = YEAR(#SelectedDate)
INNER JOIN tblBoardingCheckStatus AS bcs
ON bcs.CheckStatusID = rce.CheckStatusID
AND bcs.StatusActive = 1
INNER JOIN tblBoardingRollCallPeriods AS rcp
ON rcp.RollCallPeriodID = rce.RollCallPeriodID
AND rcp.PeriodYear = YEAR(#SelectedDate)
AND #SelectedDate BETWEEN rcp.PeriodStart AND rcp.PeriodEnd
AND rcp.RowStatus = 1
WHERE dbo.fnDateOnly(rce.DateSubmitted) = dbo.fnDateOnly(#SelectedDate)
My gridview:
Shows the following:
The data:
I want to be able to basically condense the rows in the GridView to be one student per row and the checkboxes ticked according to RollCallPeriod text.
I am playing with SQL pivots, to get the data to be as close as possible to what I am after so as to avoid code-behind, etc. However, I cannot get this to work.
select StudentID, [1],[10],[2],[3],[4],[5],[6],[7],[8],[9]
from
(
select StudentID, RollCallID, CheckStatusID
from tblBoardingRollCallEntries
unpivot
(
value for name in ([RollCallID],[StudentID],[CheckStatusID],[DateSubmitted],[StaffID])
) unpiv
) src
pivot
(
sum(RollCallPeriodID)
for RollCallPeriodID in ([1],[10],[2],[3],[4],[5],[6],[7],[8],[9])
) piv
I receive the following error:
Lookup Error - SQL Server Database Error: The type of column
"StudentID" conflicts with the type of other columns specified in the
UNPIVOT list.
Any other ideas?
Thanks
A couple of ways you can do this depending on your actual data.
This will give you the CheckStatusName as the value for the RollCallPeriod
SELECT *
FROM (
SELECT StudentName,
CheckStatusName,
RollCallPeriod
FROM [YourQueryGoesHere]
) t
PIVOT (
MAX(CheckStatusName)
FOR RollCallPeriod IN ([6:15 AM],[8:00 AM],[3:00 PM],[6:00 PM],[9:00 PM])
) p
Or you get the status and a COUNT() to show if that Student has a value for that CheckStatusName, RollCallPeriod
SELECT *
FROM (
SELECT StudentName,
CheckStatusName,
RollCallPeriod
FROM [YourQueryGoesHere]
) t
PIVOT (
COUNT(RollCallPeriod)
FOR RollCallPeriod IN ([6:15 AM],[8:00 AM],[3:00 PM],[6:00 PM],[9:00 PM])
) p
Two options:
Instead of unpivotting directly on tblBoardingRollCallEntries: first select columns cast to a VARCHAR(...) type in a derived table, then UNPIVOT the derived table. Shortened example:
select StudentID, RollCallID, CheckStatusID
from
(
SELECT ..., CAST(StudentId AS VARCHAR(128)) AS StudentId, ... FROM tblBoardingRollCallEntries)
) AS ups
unpivot
(
value for name in ([RollCallID],[StudentID],[CheckStatusID],[DateSubmitted],[StaffID])
) unpiv
Use CROSS APPLY (SELECT CAST(StudentId AS VARCHAR(128)) UNION ALL ... ) to unpivot, that way you can UNPIVOT casting the column directly to the appropriate type.
Looking for elegant (or any) solution to convert columns to rows.
Here is an example: I have a table with the following schema:
[ID] [EntityID] [Indicator1] [Indicator2] [Indicator3] ... [Indicator150]
Here is what I want to get as the result:
[ID] [EntityId] [IndicatorName] [IndicatorValue]
And the result values will be:
1 1 'Indicator1' 'Value of Indicator 1 for entity 1'
2 1 'Indicator2' 'Value of Indicator 2 for entity 1'
3 1 'Indicator3' 'Value of Indicator 3 for entity 1'
4 2 'Indicator1' 'Value of Indicator 1 for entity 2'
And so on..
Does this make sense? Do you have any suggestions on where to look and how to get it done in T-SQL?
You can use the UNPIVOT function to convert the columns into rows:
select id, entityId,
indicatorname,
indicatorvalue
from yourtable
unpivot
(
indicatorvalue
for indicatorname in (Indicator1, Indicator2, Indicator3)
) unpiv;
Note, the datatypes of the columns you are unpivoting must be the same so you might have to convert the datatypes prior to applying the unpivot.
You could also use CROSS APPLY with UNION ALL to convert the columns:
select id, entityid,
indicatorname,
indicatorvalue
from yourtable
cross apply
(
select 'Indicator1', Indicator1 union all
select 'Indicator2', Indicator2 union all
select 'Indicator3', Indicator3 union all
select 'Indicator4', Indicator4
) c (indicatorname, indicatorvalue);
Depending on your version of SQL Server you could even use CROSS APPLY with the VALUES clause:
select id, entityid,
indicatorname,
indicatorvalue
from yourtable
cross apply
(
values
('Indicator1', Indicator1),
('Indicator2', Indicator2),
('Indicator3', Indicator3),
('Indicator4', Indicator4)
) c (indicatorname, indicatorvalue);
Finally, if you have 150 columns to unpivot and you don't want to hard-code the entire query, then you could generate the sql statement using dynamic SQL:
DECLARE #colsUnpivot AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #colsUnpivot
= stuff((select ','+quotename(C.column_name)
from information_schema.columns as C
where C.table_name = 'yourtable' and
C.column_name like 'Indicator%'
for xml path('')), 1, 1, '')
set #query
= 'select id, entityId,
indicatorname,
indicatorvalue
from yourtable
unpivot
(
indicatorvalue
for indicatorname in ('+ #colsunpivot +')
) u'
exec sp_executesql #query;
well If you have 150 columns then I think that UNPIVOT is not an option. So you could use xml trick
;with CTE1 as (
select ID, EntityID, (select t.* for xml raw('row'), type) as Data
from temp1 as t
), CTE2 as (
select
C.id, C.EntityID,
F.C.value('local-name(.)', 'nvarchar(128)') as IndicatorName,
F.C.value('.', 'nvarchar(max)') as IndicatorValue
from CTE1 as c
outer apply c.Data.nodes('row/#*') as F(C)
)
select * from CTE2 where IndicatorName like 'Indicator%'
sql fiddle demo
You could also write dynamic SQL, but I like xml more - for dynamic SQL you have to have permissions to select data directly from table and that's not always an option.
UPDATEAs there a big flame in comments, I think I'll add some pros and cons of xml/dynamic SQL. I'll try to be as objective as I could and not mention elegantness and uglyness. If you got any other pros and cons, edit the answer or write in comments
cons
it's not as fast as dynamic SQL, rough tests gave me that xml is about 2.5 times slower that dynamic (it was one query on ~250000 rows table, so this estimate is no way exact). You could compare it yourself if you want, here's sqlfiddle example, on 100000 rows it was 29s (xml) vs 14s (dynamic);
may be it could be harder to understand for people not familiar with xpath;
pros
it's the same scope as your other queries, and that could be very handy. A few examples come to mind
you could query inserted and deleted tables inside your trigger (not possible with dynamic at all);
user don't have to have permissions on direct select from table. What I mean is if you have stored procedures layer and user have permissions to run sp, but don't have permissions to query tables directly, you still could use this query inside stored procedure;
you could query table variable you have populated in your scope (to pass it inside the dynamic SQL you have to either make it temporary table instead or create type and pass it as a parameter into dynamic SQL;
you can do this query inside the function (scalar or table-valued). It's not possible to use dynamic SQL inside the functions;
Just to help new readers, I've created an example to better understand #bluefeet's answer about UNPIVOT.
SELECT id
,entityId
,indicatorname
,indicatorvalue
FROM (VALUES
(1, 1, 'Value of Indicator 1 for entity 1', 'Value of Indicator 2 for entity 1', 'Value of Indicator 3 for entity 1'),
(2, 1, 'Value of Indicator 1 for entity 2', 'Value of Indicator 2 for entity 2', 'Value of Indicator 3 for entity 2'),
(3, 1, 'Value of Indicator 1 for entity 3', 'Value of Indicator 2 for entity 3', 'Value of Indicator 3 for entity 3'),
(4, 2, 'Value of Indicator 1 for entity 4', 'Value of Indicator 2 for entity 4', 'Value of Indicator 3 for entity 4')
) AS Category(ID, EntityId, Indicator1, Indicator2, Indicator3)
UNPIVOT
(
indicatorvalue
FOR indicatorname IN (Indicator1, Indicator2, Indicator3)
) UNPIV;
Just because I did not see it mentioned.
If 2016+, here is yet another option to dynamically unpivot data without actually using Dynamic SQL.
Example
Declare #YourTable Table ([ID] varchar(50),[Col1] varchar(50),[Col2] varchar(50))
Insert Into #YourTable Values
(1,'A','B')
,(2,'R','C')
,(3,'X','D')
Select A.[ID]
,Item = B.[Key]
,Value = B.[Value]
From #YourTable A
Cross Apply ( Select *
From OpenJson((Select A.* For JSON Path,Without_Array_Wrapper ))
Where [Key] not in ('ID','Other','Columns','ToExclude')
) B
Returns
ID Item Value
1 Col1 A
1 Col2 B
2 Col1 R
2 Col2 C
3 Col1 X
3 Col2 D
I needed a solution to convert columns to rows in Microsoft SQL Server, without knowing the colum names (used in trigger) and without dynamic sql (dynamic sql is too slow for use in a trigger).
I finally found this solution, which works fine:
SELECT
insRowTbl.PK,
insRowTbl.Username,
attr.insRow.value('local-name(.)', 'nvarchar(128)') as FieldName,
attr.insRow.value('.', 'nvarchar(max)') as FieldValue
FROM ( Select
i.ID as PK,
i.LastModifiedBy as Username,
convert(xml, (select i.* for xml raw)) as insRowCol
FROM inserted as i
) as insRowTbl
CROSS APPLY insRowTbl.insRowCol.nodes('/row/#*') as attr(insRow)
As you can see, I convert the row into XML (Subquery select i,* for xml raw, this converts all columns into one xml column)
Then I CROSS APPLY a function to each XML attribute of this column, so that I get one row per attribute.
Overall, this converts columns into rows, without knowing the column names and without using dynamic sql. It is fast enough for my purpose.
(Edit: I just saw Roman Pekar answer above, who is doing the same.
I used the dynamic sql trigger with cursors first, which was 10 to 100 times slower than this solution, but maybe it was caused by the cursor, not by the dynamic sql. Anyway, this solution is very simple an universal, so its definitively an option).
I am leaving this comment at this place, because I want to reference this explanation in my post about the full audit trigger, that you can find here: https://stackoverflow.com/a/43800286/4160788
DECLARE #TableName varchar(max)=NULL
SELECT #TableName=COALESCE(#TableName+',','')+t.TABLE_CATALOG+'.'+ t.TABLE_SCHEMA+'.'+o.Name
FROM sysindexes AS i
INNER JOIN sysobjects AS o ON i.id = o.id
INNER JOIN INFORMATION_SCHEMA.TABLES T ON T.TABLE_NAME=o.name
WHERE i.indid < 2
AND OBJECTPROPERTY(o.id,'IsMSShipped') = 0
AND i.rowcnt >350
AND o.xtype !='TF'
ORDER BY o.name ASC
print #tablename
You can get list of tables which has rowcounts >350 . You can see at the solution list of table as row.
The opposite of this is to flatten a column into a csv eg
SELECT STRING_AGG ([value],',') FROM STRING_SPLIT('Akio,Hiraku,Kazuo', ',')