combine multiple rows into a single row with repeating set of columns - sql

I have a table with multiple insurance policies per client. One Policy per record.
I need to represent all the policies in one row per client.
I have read all the similar questions and they may be close but I don't seem to be able to translate the answers into my situation.
The table that I have looks like this
enter code here Client-ID Ins-Company Policy-Number Start-Date
1 BCBS BSBC1 2018-01-01
1 Aetna Aetna1 2017-01-01
1 Self-Pay N/A 2016-01-01
2 Self-Pay N/A 2015-01-01
3 BCBS BCBS3 2014-01-01
3 Self-Pay N/A 2013-01-01
Expected Result:
enter code here Client-ID Ins-Co1 Policy1 Start1 Ins-Co2 Policy2 Start2 Ins-Co3 Policy3 Start3
1 BCBS BCBS1 2018-01-01 Aetna Aetna1 2017-01-01 Self-Pay N/A 2016-01-01
2 Self-Pay N/A 2015-01-01
3 BCBS BCBS3 2014-01-01 Self-Pay N/A 2013-01-01
In need to create another table with these records

I created the result I needed with the following code.
First, I created a View of the source table that added a Row Number using the following Function:
ROW_NUMBER() OVER(PARTITION BY a.Client_ID ORDER BY a.Billing_Order ASC) AS Row_No
and built the following Code
enter code hereDECLARE #sql varchar(max)
DECLARE #colList varchar(max)
--create dynamic list of columns
SELECT #colList =
STUFF
(
(
SELECT + ',' +
quotename(colName + Row_No)
FROM Credible_Client_Insurance_Raw_Data_Query
CROSS APPLY
(
SELECT 1 As Ord, 'Payer_ID' ColName UNION ALL
SELECT 2 As Ord, 'Billing_Order' UNION ALL
SELECT 3 As Ord, 'Insurance_ID' UNION ALL
SELECT 4 As Ord, 'Group_No' UNION ALL
SELECT 5 As Ord, 'Copay_Fee' UNION ALL
SELECT 6 As Ord, 'Start_Date'
) v
GROUP BY colName, Ord, Row_No
ORDER BY Row_No, Ord
for xml path(''), type
).value('/','varchar(max)'),1,1,''
)
--unpivot columns into rows and then apply pivot
SET #sql
= '
SELECT Client_ID, ' + #colList + '
FROM
(
SELECT Client_ID, ColVal,
colName + Row_No ColName
FROM Credible_Client_Insurance_Raw_Data_Query
CROSS APPLY
(
SELECT Payer_ID As ColVal, ''Payer_ID'' ColName UNION ALL
SELECT Billing_Order, ''Billing_Order'' UNION ALL
SELECT Insurance_ID, ''Insurance_ID'' UNION ALL
SELECT Group_No, ''Group_No'' UNION ALL
SELECT CAST(Copay_Fee AS VARCHAR), ''Copay_Fee'' UNION ALL
SELECT CAST(Start_Date AS VARCHAR), ''Start_Date''
) v
) A
PIVOT
(
MAX(ColVal) FOR ColName IN (' + #colList + ')
) P1 '
EXEC(#sql)
Code was copied from another question
Pivot on multiple columns based on single column
Now I have a follow up question:
Now that I created a query that provides the result I need
I need to place the result into a table.
I cannot take this query and make it into a VIEW because it starts with a Declare Statement ( Illegal for Views )
So how do I transfer this query data into a Table?

Related

Redshift split single dynamic column into multiple rows in new table

With a table like:
uid | segmentids
-------------------------+----------------------------------------
f9b6d54b-c646-4bbb-b0ec | 4454918|4455158|4455638|4455878|4455998
asd7a0s9-c646-asd7-b0ec | 1265899|1265923|1265935|1266826|1266596
gd3355ff-cjr8-assa-fke0 | 2237557|2237581|2237593
laksnfo3-kgi5-fke0-b0ec | 4454918|4455158|4455638|4455878
How to create a new table with:
uid | segmentids
-------------------------+---------------------------
f9b6d54b-c646-4bbb-b0ec | 4454918
f9b6d54b-c646-4bbb-b0ec | 1265899
f9b6d54b-c646-4bbb-b0ec | 2237557
f9b6d54b-c646-4bbb-b0ec | 4454918
f9b6d54b-c646-4bbb-b0ec | 4454918
asd7a0s9-c646-asd7-b0ec | 1265899
asd7a0s9-c646-asd7-b0ec | 1265923
asd7a0s9-c646-asd7-b0ec | 1265935
asd7a0s9-c646-asd7-b0ec | 1266826
asd7a0s9-c646-asd7-b0ec | 1266596
The number of segments are dynamic, can vary with each record.
I tried the Split function with delimiter, but it requires the index in string, which is dynamic here.
Any suggestions?
Here is the Redshift answer, it will work with up to 10 thousand segment ids values per row.
test data
create table test_split (uid varchar(50),segmentids varchar(max));
insert into test_split
values
('f9b6d54b-c646-4bbb-b0ec','4454918|4455158|4455638|4455878|4455998'),
('asd7a0s9-c646-asd7-b0ec','1265899|1265923|1265935|1266826|1266596'),
('asd7345s9-c646-asd7-b0ec','1235935|1263456|1265675696'),
('as345a0s9-c646-asd7-b0ec','12765899|12658883|12777935|144466826|1266226|12345')
;
code
with ten_numbers as (select 1 as num union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9 union select 0)
, generted_numbers AS
(
SELECT (1000 * t1.num) + (100 * t2.num) + (10 * t3.num) + t4.num AS gen_num
FROM ten_numbers AS t1
JOIN ten_numbers AS t2 ON 1 = 1
JOIN ten_numbers AS t3 ON 1 = 1
JOIN ten_numbers AS t4 ON 1 = 1
)
, splitter AS
(
SELECT *
FROM generted_numbers
WHERE gen_num BETWEEN 1 AND (SELECT max(REGEXP_COUNT(segmentids, '\\|') + 1)
FROM test_split)
)
--select * from splitter;
, expanded_input AS
(
SELECT
uid,
split_part(segmentids, '|', s.gen_num) AS segment
FROM test_split AS ts
JOIN splitter AS s ON 1 = 1
WHERE split_part(segmentids, '|', s.gen_num) <> ''
)
SELECT * FROM expanded_input;
the first 2 cte steps (ten_numbers and generated_numbers) are used to generate a number of rows, this is needed because generate_series is not supported
The next step (splitter) just takes a number of rows equal to the max number of delimiters + 1 (which is the max number of segments)
finally, we cross join splitter with the input data, take the related value using split_part and then exclude blank parts (which are caused where the row has < the max number of segments)
You can iterate over the SUPER array returned by split_to_array -- see the "Unnesting and flattening" section of this post. Using the same test_split table as the previous answer:
WITH seg_array AS
(SELECT uid,
split_to_array(segmentids, '|') segs
FROM test_split)
SELECT uid,
segmentid::int
FROM seg_array a,
a.segs AS segmentid;
Redshift now has the super data type & the split_to_array function which is similar to postgresql string_to_array
Redshift now also supports unnesting arrays through a syntax similar to a LATERAL JOIN in postgresql.
Using these techniques, we may write the same transformation in 2022 as
WITH split_up AS (
SELECT
uid
, split_to_array(segmentids) segment_array
)
SELECT
su.uid
, CAST(sid AS VARCHAR) segmentid
FROM split_up su
JOIN split_up.segment_array sid ON TRUE

Group concat in sql server for multiple column groups performance

I am currently working in SQL server 2005 and table contain million of rows.
The table have following rows and columns
ID PO_ID Event_ID Item_ID
1 22 970 123456
1 22 970 123457
1 23 970 1234589
1 22 971 12345790
1 22 971 12345792
I want to concat column item_ID for multiple column group "ID, PO_ID, Event_ID"
The output Will be like this
ID PO_ID Event_ID Item_ID
1 22 970 123456,123457
1 23 970 1234589
1 22 971 12345790,12345792
I have the following SQL query
select ID, PO_ID, Event_ID,
substring(
( SELECT ','+ Item_ID)
FROM table as a
WHERE a.ID=table.ID
AND a.PO_ID=table.PO_ID
and a.event_ID=table.event_ID
FOR XML PATH ('')
)
from table
group by ID,PO_ID,Event_ID;
But this query is really slow in terms of performance
Is there any optimized way to do this in SQL server 2005?
Any help will be appreciated.
Note : I don't have permissions to create UDF or indexes.
I would recommend writing this as:
select ID, PO_ID, Event_ID,
stuff( (select ',' + a.Item_ID)
from table a
where a.ID = t.ID and
a.PO_ID = t.PO_ID and
a.event_ID = t.event_ID
FOR XML PATH ('')
), 1, 1, ''
) as items
from (select distinct ID, PO_ID, Event_ID
from table t
) t;
Then for performance, you want an index on (ID, PO_ID, Event_ID, Item_Id).
It's possible you create an index on the table this could help you

Sorting two dimensional table by reordering rows and columns

Is it possible and if it is, how to sort two dimensional table, by reordering columns and rows, and using only these two operations, that table's biggest numbers are concentrated in top-left corner?
Any help would be very greatly appreciated.
For example, we can use this table:
Column 1 Column 2 Column 3
Row 1 2 4 5
Row 2 3 2 6
Row 3 7 2 6
Result I think would be this, but I am not sure:
Column 1 Column 2 Column 3
Row 1 7 6 2
Row 2 3 6 2
Row 3 2 5 4
For now, I only thought about summing rows and columns and sorting them to left-top descending.
More of a MatLab guy when it comes to matrix manipulations, but perhaps this may help.
Here we use a TVF to create a dynamic EAV structure. If you can't use a function, it is a small matter to go in-line.
Also, the final pivot can be dynamic if needed
Example
Declare #YourTable table (Column1 int,Column2 int,Column3 int)
Insert Into #YourTable values
(2,4,5),
(3,2,6),
(7,2,6)
;with cte as (
Select RowNr=Dense_Rank() over (Order By RowTotal Desc,Entity )
,ColNr=Dense_Rank() over (Order By ColTotal Desc,Attribute)
,Value
From (
Select *
,RowTotal = max(cast(Value as float)) over(Partition By Entity)
,ColTotal = max(cast(Value as float)) over(Partition By Attribute)
From [dbo].[udf-EAV]((Select RN=Row_Number() over (Order By (Select null)),* From #YourTable for XML RAW))
) A
)
Select [1] Col1,[2] Col2,[3] Col3
From cte
Pivot (max(Value) For [ColNr] in ([1],[2],[3]) ) p
Returns
Col1 Col2 Col3
7 6 2
3 6 2
2 5 4
The UDF if Interested
CREATE FUNCTION [dbo].[udf-EAV](#XML xml)
Returns Table
As
Return (
with cteKey(k) as (Select Top 1 xAtt.value('local-name(.)','varchar(100)') From #XML.nodes('/row') As A(xRow) Cross Apply A.xRow.nodes('./#*') As B(xAtt))
Select Entity = xRow.value('#*[1]','varchar(50)')
,Attribute = xAtt.value('local-name(.)','varchar(100)')
,Value = xAtt.value('.','varchar(max)')
From #XML.nodes('/row') As A(xRow)
Cross Apply A.xRow.nodes('./#*') As B(xAtt)
Where xAtt.value('local-name(.)','varchar(100)') Not In (Select k From cteKey)
)
-- Notes: First Field in Query will be the Entity
-- Select * From [dbo].[udf-EAV]((Select UTCDate=GetUTCDate(),* From sys.dm_os_sys_info for XML RAW))

SQL Pivot Column that has multiple values for same column

Trying to pivot table results that may have multiple rows with the same value
I have data that looks like this so far.
Nbr Person Test
33 Barry. Prim
33 Brian Sup
33 Burke RT 1st
33 Ray Add
33 Jake Add
33 Smith Add
I'm trying to pivot it so that it looks like this:
Nbr Prim Sup 1st Add Add2 Add3
33 Barry Brian Burke Ray Jake Smith
This is what I have so far with a normal pivot but it doesn't work to grab all the ones with the same value in the Test Column
CREATE TABLE #testTbl(nbr int,name varchar(20),test VARCHAR(10))
INSERT INTO #testTbl
SELECT '33','Barry','Prim'
UNION
SELECT '33','Brian','Sup'
UNION
SELECT '33','Burke','1st'
UNION
SELECT '33','Ray','Add'
UNION
SELECT '33','jake','Add'
UNION
SELECT '33','Smith','Add'
select * from (
Select *
from #testTbl
) as x
pivot(
max(name) for test in ([prim],[sup],[1st],[add])
)
as pivot1
Any help is greatly appreciated. If its not possible to have the columns output as Add Add2 and Add3 thats fine. Whatever works.
You can do so by modifying the test value using window functions:
select *
from (Select tt.name,
(test + (case when count(*) over (partition by test) = 1
then ''
else cast(row_number() over (partition by test order by (select null)) as varchar(255))
end)) as test
from testTbl tt
) as x
pivot(
max(name) for test in ([prim], [sup], [1st], [Add1], [Add2], [Add3])
) as pivot1
A SQL Fiddle is here.

Is it possible to group by a combination of rows?

I have a result that gives me a range of values to query from my database:
Start End
----- ---
1 3
5 6
137 139
From those, I need to query the database for the records in that range, which might return something like:
Id Name
----- ------
1 foo
2 bar
3 baz
Id Name
----- ------
5 foo
6 baz
Id Name
----- ------
137 foo
138 bar
139 baz
I want to group the result of those, keeping any of the id ranges since they correlate to the same thing. For example, 1-3 is the same as 137-139, so it would have a count of 2, but of course, the 'range' can be either of the 2:
RangeStart RangeEnd Count
---------- -------- -----
137 139 2
5 6 1
Also note that the order should change the grouping, so foo/bar/baz is not the same as foo/baz/bar.
How can this be accomplished?
EDIT: I have the beginning result (start,end) and I only care about the end result (RangeStart,RangeEnd,Count). I don't actually need the intermediate results, I just use them as explanation.
Here are two queries:
The first one concatenates the strings
into groups based on the ranges and
then shows the first range for each
group of strings. It also has the
total number of times the string
appeared.
The second one shows the concatenated
strings and their respective totals.
Setup:
DECLARE #Tags TABLE (
TagID INT,
Tag VARCHAR(3)
)
INSERT #Tags
SELECT 1, 'Foo' UNION ALL
SELECT 2, 'Bar' UNION ALL
SELECT 3, 'Baz' UNION ALL
SELECT 4, 'Foo' UNION ALL
SELECT 5, 'Bar' UNION ALL
SELECT 6, 'Baz'
DECLARE #Ranges TABLE (
StartRange INT,
EndRange INT
)
INSERT #Ranges
SELECT 1,3 UNION ALL
SELECT 2,3 UNION ALL
SELECT 3,4 UNION ALL
SELECT 4,6
Query To Show First Ranges and Results:
/* Get the first start and end ranges with a match and */
/* the total number of occurences of that match */
SELECT
StartRange,
EndRange,
Total
FROM (
SELECT
StartRange,
EndRange,
Csv,
ROW_NUMBER() OVER (PARTITION BY Csv ORDER BY StartRange ASC) AS RowNum,
ROW_NUMBER() OVER (PARTITION BY Csv ORDER BY StartRange DESC) AS Total
FROM (
/* For each range and its associated Tag values, */
/* Concatenate the tags together using FOR XML */
/* and the STUFF function */
SELECT
StartRange,
EndRange,
(
SELECT STUFF(
(SELECT ',' + Tag
FROM #Tags WHERE TagID BETWEEN r.StartRange AND r.EndRange
ORDER BY TagID
FOR XML PATH('')),1,1,'')
) AS Csv
FROM #Ranges r
) t1
) t2
WHERE RowNum = 1
ORDER BY StartRange, EndRange
/* Results */
StartRange EndRange Total
----------- ----------- -----
1 3 2
2 3 1
3 4 1
Query to show concatenanted strings and totals:
/* Get the concatenated tags and their respective totals */
SELECT
Csv,
COUNT(*) AS Total
FROM (
/* For each range and its associated Tag values, */
/* Concatenate the tags together using FOR XML */
/* and the STUFF function */
SELECT
StartRange,
EndRange,
(
SELECT STUFF(
(SELECT ',' + Tag
FROM #Tags WHERE TagID BETWEEN r.StartRange AND r.EndRange
ORDER BY TagID
FOR XML PATH('')),1,1,'')
) AS Csv
FROM #Ranges r
) t1
GROUP BY Csv
ORDER BY Csv
/* Results */
Csv Total
------------ -----------
Bar,Baz 1
Baz,Foo 1
Foo,Bar,Baz 2
String concatentation method courtesy of Jeremiah Peschka