Creating rows when column name repeats for pivot - sql-server

I have a CSV file that imports into SQL in the below format, theres a key field then repeating values that need to be on seperate rows, each new part needs to be a new row can anyone suggest some SQL to help, I've managed to pivot the data however this only returns the first parts data.
Current CSV IMPORT
Header
Data
Load number
220511
Part
1234
Lot
AB14
Qty
10
Part
4567
Lot
HD14
Qty
19
Current Pivot
Load Number
Part
Lot
QTY
220511
1234
AB14
10
Required Pivot
Load Number
Part
Lot
QTY
220511
1234
AB14
10
220511
4567
HD14
19
Current Code
Select [Load number ],
[part number ],
[lot number ],
[quantity ]
From
(Select LTRIM(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(Header,'Item',''),0,''),1,''),2,''),3,''),4,''),5,''),6,''),7,''),8,''),9,'')) as HEADER, Data From (Select LTRIM(SUBSTRING([column1],1,CASE CHARINDEX(':', [column1])WHEN 0
THEN LEN([column1])
ELSE CHARINDEX(':', [column1]) - 1
END)) AS HEADER ,LTRIM(SUBSTRING([column1], CASE CHARINDEX(':', [column1])
WHEN 0
THEN LEN([column1]) + 1
ELSE CHARINDEX(':', [column1]) + 1
END, 1000)) AS DATA FROM [BCW_TREAT].[dbo].[DMSIMPORTLOAD]) as d
Where HEADER = 'Load number' or HEADER like '%part%' or HEADER like '%lot%'or HEADER like '%quantity%' or Data > '0' or Data <> '""') b
pivot
(max(DATA)
for HEADER in ([Load number ],
[part number ],
[lot number ],
[quantity ])) piv

based on this question TSQL Pivot without aggregate function, all we have to do is to create a column like CustomerID, that's what the rn column do, so the result would be:
SELECT t.Data [Load number]
,Part
,Lot
,Qty
FROM (
SELECT *
,ROW_NUMBER() OVER (
PARTITION BY Header ORDER BY Header
) AS rn
FROM #temp
) AS SourceTable
PIVOT(max(data) FOR Header IN (
[Load number]
,[Part]
,[Lot]
,[Qty]
)) AS PivotTable
JOIN #temp t ON Header = 'Load number'

Related

Show records in three tables with defiate number of rows in each table in SSRS report

I have a table in report like
I want to show the records in three tables on every page, each table contains only 20 records.
Page1:
Page2:
How can I achieve this type of pattern?
I can think of 2 ways to do this, as a MATRIX style report where the column group is your columns, and as a normal table where you JOIN the data to produce 3 copies of name, ID, and any other fields you want. The MATRIX style is definitely more elegant and flexible, but the normal table might be easier for customers to modify if you're turning the report over to power users.
Both solutions start with tagging the data with PAGE, ROW, and COLUMN information. Note that I'm sorting on NAME, but you could sort on any field. Also note that this solution does not depend on your ID being sequential and in the order you want, it generates it's own sequence numbers based on NAME or whatever else you choose.
In this demo I'm setting RowsPerPage and NumberofColumns as hard coded constants, but they could easily be user selected parameters if you use the MATRIX format.
DECLARE #RowsPerPage INT = 20
DECLARE #Cols INT = 3
;with
--Fake data generation BEGIN
cteSampleSize as (SELECT TOP 70 ROW_NUMBER () OVER (ORDER BY O.name) as ID
FROM sys.objects as O
), cteFakeData as (
SELECT N.ID, CONCAT(CHAR(65 + N.ID / 26), CHAR(65 + ((N.ID -1) % 26))
--, CHAR(65 + ((N.ID ) % 26))
) as Name
FROM cteSampleSize as N
),
--Fake data generation END, real processing begins below
cteNumbered as ( -- We can't count on ID being sequential and in the order we want!
SELECT D.*, ROW_NUMBER () OVER (ORDER BY D.Name) as SeqNum
--Replace ORDER BY D.Name with ORDER BY D.{Whatever field}
FROM cteFakeData as D --Replace cteFakeData with your real data source
), ctePaged as (
SELECT D.*
, 1+ FLOOR((D.SeqNum -1) / (#RowsPerPage*#Cols)) as PageNum
, 1+ ((D.SeqNum -1) % #RowsPerPage) as RowNum
, 1+ FLOOR(((D.SeqNum-1) % (#RowsPerPage*#Cols) ) / #RowsPerPage) as ColNum
FROM cteNumbered as D
)
--FINAL - use this for MATRIX reports (best)
SELECT * FROM ctePaged ORDER BY SeqNum
If you want to use the JOIN method to allow this in a normal table, replace the --FINAL query above with this one. Note that it's pretty finicky, so test it with several degrees of fullness in the final report. I tested with 70 and 90 rows of sample data so I had a partial first column and a full first and partial second.
--FINAL - use this for TABLE reports (simpler)
SELECT C1.PageNum , C1.RowNum , C1.ID as C1_ID, C1.Name as C1_Name
, C2.ID as C2_ID, C2.Name as C2_Name
, C3.ID as C3_ID, C3.Name as C3_Name
FROM ctePaged as C1 LEFT OUTER JOIN ctePaged as C2
ON C1.PageNum = C2.PageNum AND C1.RowNum = C2.RowNum
AND C1.ColNum = 1 AND (C2.ColNum = 2 OR C2.ColNum IS NULL)
LEFT OUTER JOIN ctePaged as C3 ON C1.PageNum = C3.PageNum
AND C1.RowNum = C3.RowNum AND (C3.ColNum = 3 OR C3.ColNum IS NULL)
WHERE C1.ColNum = 1
1) Add the dataset with the below query to get Page number and Table number. You can change the number 20 and 60 as per requirement. In my case, I need 20 records per section and having 3 sections, so total records per page are 60.
Select *,(ROW_NUMBER ( ) OVER ( partition by PageNumber order by Id )-1)/20 AS TableNumber from (
Select (ROW_NUMBER ( ) OVER ( order by Id )-1)/60 AS PageNumber
,* from Numbers
)Src
2)Add the table of one column and select the prepared dataset.
3)Add PageNumber in Group expression for Details group.
4)Add the Column parent group by right-clicking on detail row. Select Group by TableNumber.
5) Delete the first two rows. Select Delete rows only.
6) Add one more table and select the ID and Name.
7) Drag this newly created table into the cell of the previously created table. And increase the size of the table.
Result:
Each table section contains 20 records. and it will continue in next pages also.

Subtract 2 Rows into Column multiple times and link back to header

I have some terrible data that I need to turn into something that means something. I realise this isn't the best way to show I haven't used StackOverflow in ages and I am not sure on syntax.
I have tried to write some queries but really I am not sure where to start on this sort of query so any help would be much appreciated, Thanks.
I have a header table which links to a details table. In the details table I have 3 sets of 2 records where I want to get the difference between two columns.
Header Table
headerId
1
2
Detail Table
detailid|headerId|name|totalElapsedMs
1|1|Request1|100
2|1|Response1|1000
3|1|Request2|1100
4|1|Response2|1800
5|1|Request3|2000
6|1|Response3|2600
Results
I want to subtract the rows that match each other and then pivot them up to the header row as shown below.
headerId|Request1ElapsedMs|Request2ElapsedMs|Request3ElapsedMs
1|900|700|600
I think it's fairly straightforward to use the PIVOT operator on the Detail table to get the output you want. Assuming #Detail is your source table, something like:
SELECT
HeaderID
, COALESCE( [Response1], 0 ) - COALESCE( [Request1], 0 ) AS Request1ElapsedMs
, COALESCE( [Response2], 0 ) - COALESCE( [Request2], 0 ) AS Request2ElapsedMs
, COALESCE( [Response3], 0 ) - COALESCE( [Request3], 0 ) AS Request3ElapsedMs
FROM
(SELECT HeaderID, Name, TotalElapsedMs FROM #Detail) AS Detail
PIVOT (SUM( TotalElapsedMs ) FOR Name IN ( [Request1], [Response1], [Request2], [Response2], [Request3], [Response3] )) AS PivotedData
There are many examples of Pivot and/or Dynamic Pivot.
The only trick here is to flip the sign, and then aggregate
Example
Declare #YourTable Table ([detailid] int,[headerId] int,[name] varchar(50),[totalElapsedMs] int)
Insert Into #YourTable Values
(1,1,'Request1',100)
,(2,1,'Response1',1000)
,(3,1,'Request2',1100)
,(4,1,'Response2',1800)
,(5,1,'Request3',2000)
,(6,1,'Response3',2600)
Select *
From (
Select HeaderId
,Item = concat('Request',replace(replace(name,'Request',''),'Response',''),'ElapsedMs')
,Value = [totalElapsedMs]*IIF(left(name,3)='Res',1,-1)
From #YourTable
) src
Pivot (sum(Value) for Item in ([Request1ElapsedMs],[Request2ElapsedMs],[Request3ElapsedMs]) ) pvt
Returns
HeaderId Request1ElapsedMs Request2ElapsedMs Request3ElapsedMs
1 900 700 600
If it helps, the subquery "feeding" the pivot generates
HeaderId Item Value
1 Request1ElapsedMs -100
1 Request1ElapsedMs 1000
1 Request2ElapsedMs -1100
1 Request2ElapsedMs 1800
1 Request3ElapsedMs -2000
1 Request3ElapsedMs 2600

unique chat records sql

I have DB which having 5 column as follows:
message_id
user_id_send
user_id_rec
message_date
message_details
Looking for a SQL Serve Query, I want to Filter Results from two columns (user_id_send,user_id_rec)for Given User ID based on following constrains:
Get the Latest Record (filtered on date or message_id)
Only Unique Records (1 - 2 , 2 - 1 are same so only one record will be returned which ever is the latest one)
Ordered by Descending based on message_id
SQL Query
The main purpose of this query is to get records of user_id to find out to whom he has sent messages and from whom he had received messages.
I have also attached the sheet for your reference.
Here is my try
WITH t
AS (SELECT *
FROM messages
WHERE user_id_sender = 1)
SELECT DISTINCT user_id_reciever,
*
FROM t;
WITH h
AS (SELECT *
FROM messages
WHERE user_id_reciever = 1)
SELECT DISTINCT user_id_sender,
*
FROM h;
;WITH tmpMsg AS (
SELECT M2.message_id
,M2.user_id_receiver
,M2.user_id_sender
,M2.message_date
,M2.message_details
,ROW_NUMBER() OVER (PARTITION BY user_id_receiver+user_id_sender ORDER BY message_date DESC) AS 'RowNum'
FROM messages M2
WHERE M2.user_id_receiver = 1
OR M2.user_id_sender = 1
)
SELECT T.message_id
,T.user_id_receiver
,T.user_id_sender
,T.message_date
,T.message_details
FROM tmpMsg T
WHERE RowNum <= 1
The above should fetch you the results you are looking for when you query for a particular user_id (replace the 1 with parameter e.g. #p_user_id). The user_id_receiver+user_id_sender in the PARTITION clause ensure that records with user id combinations such as 1 - 2, 2 - 1 are not selected twice.
Hope this helps.
select * from
(
select ROW_NUMBER() over (order by message_date DESC) as rowno,
* from messages
where user_id_receiver = 1
--order by message_date DESC
) T where T.rowno = 1
UNION ALL
select * from
(
select ROW_NUMBER() over (order by message_date DESC) as rowno,
* from messages
where user_id_sender = 1
-- order by message_date DESC
) T where T.rowno = 1
Explanation: For each group of user_id_sender, it orders internally by message_date desc, and then adds row numbers, and we only want the first one (chronologically last). Then do the same for user_id_receiver, and union the results together to get 1 result set with all the desired rows. You can then add your own order by clause and additional where conditions at the end as required.
Of course, this only works for any 1 user_id at a time (replace =1 with #user_id).
To get a result from all user_id's at once, is a totally different query, so I hope this helps?

Putting Data Table into 2 columns

I have a table that contains photos. One photo and its description in each record. I want to create a temp table that has two photos/descriptions per record. So, I need to create a report that has photos displayed in two columns.
This is what I have:
1 Photo1, Description1
2 Photo2, Description2
3 Photo3, Description3
4 Photo4, Description4
Here is what I am expecting:
Photo1, Description1, Photo2, Description2
Photo3, Description3, Photo4, Description4
How can I get there using a stored procedure in SQL Server 2012?
You could use Modulo like #Sean Lange mentioned, but you will want to join back to your table something like this:
;WITH NumberedPhotos
AS (
SELECT photo_name
,photo_desc
,ROW_NUMBER() OVER (
ORDER BY Photo_ID
) AS RowNum
FROM photo_info
)
SELECT
t.photo_name
,t.photo_desc
,n.photo_name
,n.photo_desc
FROM NumberedPhotos n
LEFT JOIN NumberedPhotos AS t ON n.rownum = t.rownum + 1
WHERE n.rownum % 2 = 0;
SQL Fiddle Demo
You didn't provide a lot of details here but here is a possibility. The first cte adds a row number to every row so we can ensure we have a consistent numbering pattern. Then we have a cte to retrieve the odd numbers followed by another cte to get the even numbers. Then we just join them together.
with NumberedPhotos as
(
select Photo
, Description
, ROW_NUMBER() over(order by PhotoID) as RowNum
from SomeTable
)
, OddNumbers as
(
select Photo
, Description
, RowNum
from NumberedPhotos
where RowNum % 2 = 1
)
, EvenNumbers as
(
select Photo
, Description
, RowNum
from NumberedPhotos
where RowNum % 2 = 0
)
select o.Photo
, o.Description
, e.Photo
, e.Description
from OddNumbers o
left join EvenNumbers e on o.RowNum = e.RowNum - 1

TSQL matching the first instances of multiple values in a resultset

Say I have part of a large query, as below, that returns a resultset with multiple rows of the same key information (PolNum) with different value information (PolPremium) in a random order.
Would it be possible to select the first matching PolNum fields and sum up the PolPremium. In this case I know that there are 2 PolNumber's used so given the screenshot of the resultset (yes I know it starts at 14 for illustration purposes) and return the first values and sum the result.
First match for PolNum 000035789547
(ROW 14) PolPremium - 32.00
First match for PolNum 000035789547
(ROW 16) PolPremium - 706043.00
Total summed should be 32.00 + 706043.00 = 706072.00
Query
OUTER APPLY
(
SELECT PolNum, PolPremium
FROM PN20
WHERE PolNum IN(SELECT PolNum FROM SvcPlanPolicyView
WHERE SvcPlanPolicyView.ControlNum IN (SELECT val AS ServedCoverages FROM ufn_SplitMax(
(SELECT TOP 1 ServicedCoverages FROM SV91 WHERE SV91.AccountKey = 3113413), ';')))
ORDER BY PN20.PolEffDate DESC
}
Resultset
Suppose that pic if the final result your query produces. Then you can do something like:
DECLARE #t TABLE
(
PolNum VARCHAR(20) ,
PolPremium MONEY
)
INSERT INTO #t
VALUES ( '000035789547', 32 ),
( '000035789547', 76 ),
( '000071709897', 706043.00 ),
( '000071709897', 1706043.00 )
SELECT t.PolNum ,
SUM(PolPremium) AS PolPremium
FROM ( SELECT * ,
ROW_NUMBER() OVER ( PARTITION BY PolNum ORDER BY PolPremium ) AS rn
FROM #t
) t
WHERE rn = 1
GROUP BY GROUPING SETS(t.PolNum, ( ))
Output:
PolNum PolPremium
000035789547 32.00
000071709897 706043.00
NULL 706075.00
Just replace #t with your query. Also I assume that row with minimum of premium is the first. You could probably do filtering top row in outer apply part but it really not clear for me what is going on there without some sample data.

Resources