Concatenate multiple rows to one big row - sql

I have two tables in my database, with this sample data:
table 1: main
m_id eID sDate eDate
1 75 2022-12-01 NULL
table 2: details
m_id cc_id cu_id perc
1 1 1 40
1 1 2 40
1 1 3 20
Here's what I would like to achieve in SQL Server:
m_id eID sDate eDate cc_id^1 cu_id^1 perc^1 cc_id^2 cu_id^2 perc^2 cc_id^3 cu_id^3 perc^3
1 75 2022-12-01 NULL 1 1 40 1 2 40 1 3 20
So, the three rows in the 'details' table should be concatenated to the single row in the 'main' table.
I read about and tried the PIVOT Function, but I think it's not exactly what I'm looking for. To me, it seems PIVOT is using each unique value in the 'details' table as column header and then counts the number of instances of it. For example like this:
m_id eID sDate eDate 40
1 75 2022-12-01 NULL 2
So, basically using 40 as a column header and then fill its value with 2, as there are two instances of 40 in the perc column in the 'details' table.
I spent an entire day searching via Google and trying the PIVOT function without luck.

The only way I can think of it is this, (I would imagine that someone else will have a better solution!), BUT you NEED to know the values of [cu_id] ahead of time, and without knowing what the key is on Table 2, this is the best that I can do:
SELECT
Main.m_id,
Main.eID,
Main.sDate,
Main.eDate,
[Details1].[cc_id] AS [cc_id^1],
[Details1].[cu_id] AS [cu_id^1],
[Details1].[perc] AS [perc^1] ,
[Details2].[cc_id] AS [cc_id^2],
[Details2].[cu_id] AS [cu_id^2],
[Details2].[perc] AS [perc^2],
[Details3].[cc_id] AS [cc_id^3],
[Details3].[cu_id] AS [cu_id^3],
[Details3].[perc] AS [perc^3]
FROM [main] AS Main
INNER JOIN [Details] AS [Details1] ON Main.[m_id] = [Details1].[m_id]
INNER JOIN [Details] AS [Details2] ON Main.[m_id] = [Details1].[m_id]
INNER JOIN [Details] AS [Details3] ON Main.[m_id] = [Details3].[m_id]
WHERE
[Details1].[cu_id] = 1
AND
[Details2].[cu_id] = 2
AND
[Details3].[cu_id] = 3
I get the correct results, IF I know ahead of time what the values of cu_id are.

Many thanks for your help, Larnu and SWR!
#SWR unfortunately, the values for cu_id are not known ahead of time. They basically could be any integer. But thanks for your help though!
#Larnu, Thanks to you as well! I didn't post any of my pivot queries, as they got me nowhere near to the expected result. With the link to your other post, I was able to build this query.
WITH RNs AS(
SELECT h.clv_id, h.bed_id, h.wns_id, h.dvb_id, h.con_id, h.DateFrom, h.DateTo, d.kpl_id, d.kdr_id, d.dimension3, d.dimension4, d.dimension5, d.percentage
,ROW_NUMBER() OVER (PARTITION BY d.clv_id ORDER BY (SELECT NULL)) AS RN
FROM clv_hoofd h INNER JOIN clv_details d ON h.clv_id = d.clv_id
WHERE h.clv_id = 1094)
SELECT clv_id, bed_id, wns_id, dvb_id, con_id, DateFrom, DateTo,
CASE RN WHEN 1 THEN kpl_id END AS 'kpl^1',
CASE RN WHEN 1 THEN kdr_id END AS 'kdr^1',
CASE RN WHEN 1 THEN dimension3 END AS 'dim3^1',
CASE RN WHEN 1 THEN dimension4 END AS 'dim4^1',
CASE RN WHEN 1 THEN dimension5 END AS 'dim5^1',
CASE RN WHEN 1 THEN percentage END AS 'perc^1',
CASE RN WHEN 2 THEN kpl_id END AS 'kpl^2',
CASE RN WHEN 2 THEN kdr_id END AS 'kdr^2',
CASE RN WHEN 2 THEN dimension3 END AS 'dim3^2',
CASE RN WHEN 2 THEN dimension4 END AS 'dim4^2',
CASE RN WHEN 2 THEN dimension5 END AS 'dim5^2',
CASE RN WHEN 2 THEN percentage END AS 'perc^2',
CASE RN WHEN 3 THEN kpl_id END AS 'kpl^3',
CASE RN WHEN 3 THEN kdr_id END AS 'kdr^3',
CASE RN WHEN 3 THEN dimension3 END AS 'dim3^3',
CASE RN WHEN 3 THEN dimension4 END AS 'dim4^3',
CASE RN WHEN 3 THEN dimension5 END AS 'dim5^3',
CASE RN WHEN 3 THEN percentage END AS 'perc^3',
CASE RN WHEN 4 THEN kpl_id END AS 'kpl^4',
CASE RN WHEN 4 THEN kdr_id END AS 'kdr^4',
CASE RN WHEN 4 THEN dimension3 END AS 'dim3^4',
CASE RN WHEN 4 THEN dimension4 END AS 'dim4^4',
CASE RN WHEN 4 THEN dimension5 END AS 'dim5^4',
CASE RN WHEN 4 THEN percentage END AS 'perc^4',
CASE RN WHEN 5 THEN kpl_id END AS 'kpl^5',
CASE RN WHEN 5 THEN kdr_id END AS 'kdr^5',
CASE RN WHEN 5 THEN dimension3 END AS 'dim3^5',
CASE RN WHEN 5 THEN dimension4 END AS 'dim4^5',
CASE RN WHEN 5 THEN dimension5 END AS 'dim5^5',
CASE RN WHEN 5 THEN percentage END AS 'perc^5',
CASE RN WHEN 6 THEN kpl_id END AS 'kpl^6',
CASE RN WHEN 6 THEN kdr_id END AS 'kdr^6',
CASE RN WHEN 6 THEN dimension3 END AS 'dim3^6',
CASE RN WHEN 6 THEN dimension4 END AS 'dim4^6',
CASE RN WHEN 6 THEN dimension5 END AS 'dim5^6',
CASE RN WHEN 6 THEN percentage END AS 'perc^6'
FROM RNs R
This returns six rows, doing it like this:
kpl_id^1 kdr_id^1 dimension3^1 dimension4^1 dimension5^1 percentage^1 kpl_id^2 kdr_id^2 dimension3^2 dimension4^2 dimension5^2 percentage^2
2 1 00000 01 NVT 50.00 NULL NULL NULL NULL NULL NULL
NULL NULL NULL NULL NULL NULL 1 1 00000 02 NVT 10.00
...
This query comes from my real world database, hence the different names for tables and columns.
Thanks in advance!

Related

How to set an incrementing flag column for related rows?

I am trying to create a flag column called "Related" to use in reporting to highlight specific rows that are related based on the ID column (1 = related, NULL = not related). The original table "table1" looks like below:
Name ID Related
--------------------------------
Jack 101 NULL
John 101 NULL
Pat 105 NULL
Ben 106 NULL
Jordan 106 NULL
George 300 NULL
Alan 500 NULL
Bill 200 NULL
Bob 200 NULL
I then used this UPDATE statement below:
UPDATE a
SET Related = 1
FROM table1 a
JOIN (SELECT ID FROM table1 GROUP BY ID HAVING COUNT(*) > 1) b
ON a.ID = b.ID
Below is the result of this update statement:
Name ID Related
--------------------------------
Jack 101 1
John 101 1
Pat 105 NULL
Ben 106 1
Jordan 106 1
George 300 NULL
Alan 500 NULL
Bill 200 1
Bob 200 1
This gets me close but I need for it to instead of assigning the number 1 to each related row, to increment the number for each set of related rows based on their different ID column values.
Desired result:
Name ID Related
--------------------------------
Jack 101 1
John 101 1
Pat 105 NULL
Ben 106 2
Jordan 106 2
George 300 NULL
Alan 500 NULL
Bill 200 3
Bob 200 3
This is a possible solution using dense_rank to number your related values and an updateable CTE
with r as (
select id
from t
group by id having Count(*) > 1
),
n as (
select t.id, t.related, Dense_Rank() over (order by r.id) r
from r
join t on t.id = r.id
)
update n set related = r
You can do this without a self-join, just using window functions in a CTE, and updating the CTE directly:
WITH tCounted AS (
SELECT
t.id,
t.related,
c = COUNT(*) OVER (PARTITION BY r.id)
FROM t
),
tWithRelated as (
SELECT
t.id,
t.related,
rn = DENSE_RANK() OVER (ORDER BY r.id)
FROM tCounted
WHERE c > 1
)
UPDATE tWithRelated
SET related = rn;
Use an updateable CTE - comments explain the logic.
with cte1 as (
select [Name], ID, Related
-- Get the count within the id partition, less 1 as specified
, count(*) over (partition by id) - 1 cnt
-- Get the row number within the id partition
, row_number() over (partition by id order by id) rn
from #Test
), cte2 as (
select [Name], ID, Related, cnt, rn
-- Add 1 *only* if the count is > 0 *and* its the first row in the id partition
, case when cnt > 0 then sum(case when cnt > 0 and rn = 1 then 1 else 0 end) over (order by id) else null end NewRelated
from cte1
)
update cte2 set Related = NewRelated;
This doesn't assume Related is already null and works for more than 2 rows for any given ID.
It does assume that one can order by the ID column - even though the data provided doesn't do that.

How i want make 3 rows of the same primary key into 1 row by checking and choose its value using case

For example here, from this table
key | status
1001 | A
1001 | D
1001 | C
the hierarchy will be C>D>A
If the the stats contain C as the value, the person status will become C in one row. It will be like this:
key | status
1001 | C
it will ignore D and A because it has value C. If it doesn't has C,then it will check for D first before A.
So, how can i do that? I dont how to make the 3 row into 1. I tried using
''' CASE WHEN STATUS IN('C')THEN 'C'
ELSE WHEN STATUS IN('D') THEN 'D'
ELSE WHEN STATUS IN('A') THEN 'A'
END AS STATUS '''
But it give error and still can't make the row into 1
You can try the below -
select key, status
from tablname
order by case when status='C' then 1 when status='D' then 2 when status='A' then 3 else 99 end
FETCH FIRST 1 ROW
You can use analytical function row_number as follows:
Select * from
(Select t.*,
Row_number() over (partition by key order by case when status = 'C' then 1
when status = 'D' then 2
when status = 'A' then 3 end) as rn
From your_table)
Where rn = 1;

Updating Rows in a normalized table

normalized table
ID SEQ Type Value Flag
1 1 a 100 -
1 2 a 200 -
1 3 a 250 -
1 4 b 200 -
2 1 a 150 -
2 2 b 100 -
2 3 b 200 -
How do I write a single update statement such that the resulting table is populated as follows
ID SEQ Type Value Flag
1 1 a 100 valid
1 2 a 200 repeat
1 3 a 250 repeat
1 4 b 200 valid
2 1 a 150 valid
2 2 b 100 valid
2 3 b 200 repeat
Edit: included seq column
only the first occurence of the value for a type for a ID group should have the valid flag
should it be written as two separate update statements?
can someone clarify me?
Much appreciated
Populate the table first using row_number() and then update the table.
Option 1:
select
Id,
Type,
Value,
null as Flag,
row_number() over (partition by ID, Type order by SEQ) as rnk
from yourTable
then you can use update
update yourTable
set flag = case
when rnk = 1 then 'valid'
else 'repeat'
end
Option 2:
You may be able to do without using update statement as following
select
Id,
SEQ,
Type,
Value,
case
when rnk = 1 then 'valid'
else 'repeat'
end as flag
from
(
select
Id,
SEQ,
Type,
Value,
row_number() over (partition by ID, Type order by SEQ) as rnk
from yourTable
) val

Sort rows for duplicate ID numbers

I have been trying to find ways to sort rows based on duplicate ID numbers per below but have been unsuccessful.
ID SortOrder PersonID
1 0 100
2 1 100
3 0 200
4 0 200
5 1 200
I am trying to sort the sortOrder column so the number 1 will display at the top for every PersonID. So the end results are like below:
ID SortOrder PersonID
1 1 100
2 0 100
3 1 200
4 0 200
5 0 200
Any Suggestions? Thanks!
You can use CASE like this.
ORDER BY PersonID,CASE WHEN SortOrder = 1 THEN 1 ELSE 2 END
You can change the CASE appropriately, if you want to change how following rows after SortOrder = 1 should be ordered for the same PersonID.
Like ORDER BY PersonID,CASE SortOrder WHEN 1 THEN 1 WHEN 0 THEN 2 ELSE 3 END
One other solution:
select id,SortOrder,PersonId from
(select id,SortOrder,PersonId,row_number() over (partition by PersonId order by SortOrder desc) as rn from table) A
order by A.rn asc
Based on your comments regarding wanting the ID value to stay in constant order, you're likely looking for something like this:
Select Row_Number() Over (Order By PersonID Asc, SortOrder Desc) As ID,
SortOrder,
PersonID
From YourTable
Order By PersonID Asc, SortOrder Desc
But, I'd caution against naming the ROW_NUMBER field ID, as it can be misleading since it's not actually the unique identifier for the stored record.

Return results where first entry is 1 and all subsequent rows are 0

I m working on weird SQL query
Patient_ID Count order_no
1 1 1
2 1 2
2 0 3
2 0 4
3 1 5
3 0 6
where I need to count the patient as above, for every new patient , the count column is 1.
If repeated , the below entry it should be 0
I m confused how should make that work in SQL
In order to make the first entry 1 and all subsuqent entries 0, I believe you need a ranking with partition by the order number. Please checkout the sqlfiddle below to test results.
http://www.sqlfiddle.com/#!3/4e2e2/17/0
SELECT
patient_id
,CASE WHEN r.rank = 1
THEN 1
ELSE 0
END
, order_number
FROM
(
SELECT
order_number
,patient_id
,ROW_NUMBER() OVER (PARTITION BY patient_id ORDER BY order_number)[rank]
FROM
PatientTable
)r