SQL query: Recode value as new variable - sql-server

I am using a database that consist of answers to customer questionnaires. My problem is that while the customers has been asked several questions each, the number and specific questions vary and each question has its own record. So each questionnaire has three questions.
I have grouped the question types and want one record for each questionnaire with all the answers.
If qnumber [1,2,3],[4,5,6],[7,8,9] are the same and the info is like this
ID,qnumber,avalue
1,1,4
1,4,5
1,7,6
2,2,5
2,5,6
2,8,7
3,3,7
3,6,8
3,9,9
I want to construct the query so a get a result like this:
ID,q1,q2,q3
1,4,5,6
2,5,6,7
3,6,7,8
Is that even possible?

Try this
;With cte(ID,qnumber,avalue )
AS
(
SELECT 1,1,4 UNION ALL
SELECT 1,4,5 UNION ALL
SELECT 1,7,6 UNION ALL
SELECT 2,2,5 UNION ALL
SELECT 2,5,6 UNION ALL
SELECT 2,8,7 UNION ALL
SELECT 3,3,7 UNION ALL
SELECT 3,6,8 UNION ALL
SELECT 3,9,9
)
SELECT Cstring AS CombinedValue
,SUBSTRING(Cstring, 0, CHARINDEX(',', Cstring)) AS ID
,SUBSTRING(Cstring, CHARINDEX(',', Cstring) + 1, CHARINDEX(',', Cstring) - 1) AS Q1
,SUBSTRING(Cstring, CHARINDEX(',', Cstring) + 5, CHARINDEX(',', Cstring) - 1) AS Q2
,SUBSTRING(Cstring, CHARINDEX(',', Cstring) + 9, CHARINDEX(',', Cstring) - 1) AS Q2
FROM (
SELECT DISTINCT STUFF((
SELECT ',' + CAST(qnumber AS VARCHAR) + ',' + CAST(avalue AS VARCHAR)
FROM cte i
WHERE i.ID = o.ID
FOR XML PATH('')
), 1, 1, '') AS Cstring
FROM cte o
) DT
Result
CombinedValue ID Q1 Q2 Q2
------------------------------
1,4,4,5,7,6 1 4 5 6
2,5,5,6,8,7 2 5 6 7
3,7,6,8,9,9 3 7 8 9

CREATE TABLE #Temp
(
ID INT,
qnumber INT,
avalue INT
)
INSERT #Temp
VALUES
(1,1,4),
(1,4,5),
(1,7,6),
(2,2,5),
(2,5,6),
(2,8,7),
(3,3,7),
(3,6,8),
(3,9,9)
SELECT ID, [1] AS q1, [2] AS q2, [3] AS q3
FROM (
SELECT ID, [1], [2], [3]
FROM (
SELECT ID,
CASE WHEN qnumber IN (1, 2, 3) THEN 1
WHEN qnumber IN (4, 5, 6) THEN 2
WHEN qnumber IN (7, 8, 9) THEN 3
END AS qnumber,
avalue
FROM #Temp) AS SourceTable
PIVOT
(AVG(avalue)
FOR qnumber IN ([1], [2], [3])) AS PivotTable
) t
DROP TABLE #Temp

Related

How to group ID's by score in different types in SQL Server

I have a table like this:
ID Type Score
-------------------
5 1 100
8 1 200
3 1 300
8 2 100
3 2 200
5 2 300
How do I sort them by descending score (to give them a ranking for that Type) and then create a table which a column for each Type where the ID's positions are shown such as:
ID Type1 Type2
--------------------
3 1st 2nd
5 3rd 1st
8 2nd 3rd
So far I am able able to do this by explicitly declaring the Type number such as:
SELECT ROW_NUMBER() OVER(ORDER BY Score DESC) AS Rank, ID
FROM Table
WHERE Type = 1
This returns a rank for each ID when Type is 1.
How do I join this together with the same result when Type is 2? And how do I do this for any number of types?
There are a number of ways to tackled this. My choice would be to use conditional aggregation. Here is how this might look. If you need a dynamic number of types that can be accomplished also but is a little trickier.
declare #Something table
(
ID int
, Type int
, Score int
)
;
insert #Something values
(5, 1, 100)
, (8, 1, 200)
, (3, 1, 300)
, (8, 2, 100)
, (3, 2, 200)
, (5, 2, 300)
;
with SortedValues as
(
select *
, RowNum = ROW_NUMBER() over (partition by Type order by Score)
from #Something
)
select ID
, Type1 = max(case when Type = 1 then RowNum end)
, Type2 = max(case when Type = 2 then RowNum end)
from SortedValues
group by ID
order by ID
;
-- EDIT --
I realized you said you need to have this work for any number of Types. Most people around SO like to ue a dynamic pivot. I personally find the syntax for pivot to be very obtuse. I prefer to build a dynamic version of conditional aggregation for this type of thing. Here is how you can use dynamic sql to generate the results for any number of Types.
Note I had to switch to using a temp table because a table variable would not be available in the scope of the dynamic sql unless it is declared inside the dynamic sql.
if OBJECT_ID('tempdb..#Something') is not null
drop table #Something
create table #Something
(
ID int
, Type int
, Score int
)
;
insert #Something values
(5, 1, 100)
, (8, 1, 200)
, (3, 1, 300)
, (8, 2, 100)
, (3, 2, 200)
, (5, 2, 300)
;
declare #StaticPortion nvarchar(2000) =
'with SortedValues as
(
select *, ROW_NUMBER() over(partition by Type order by Score) as RowNum
from #Something
)
select ID';
declare #DynamicPortion nvarchar(max) = '';
with E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E2
)
select #DynamicPortion = #DynamicPortion +
', MAX(Case when Type = ' + CAST(N as varchar(6)) + ' then RowNum end) as Type' + CAST(N as varchar(6)) + CHAR(10)
from cteTally t
where t.N <=
(
select Count(distinct Type)
from #Something
)
declare #FinalStaticPortion nvarchar(2000) = ' from SortedValues
group by ID
order by ID';
select #StaticPortion + #DynamicPortion + #FinalStaticPortion
declare #SqlToExecute nvarchar(max) = #StaticPortion + #DynamicPortion + #FinalStaticPortion;
select #SqlToExecute
exec sp_executesql #SqlToExecute

Consolidate rows of data in SQL Server

I have multiple rows of order data which i need to consolidate in one row per part.
An example is as follows:
OrderNum PartNum Qty
-------------------------------
1 24 2
2 25 10
3 24 5
4 24 10
This then needs to be consolidated into:
OrderNum PartNum Qty
-------------------------------
1, 3, 4 24 17
2 25 10
Does anybody have any ideas how I can do this?
I have had a look around online but cannot find a solution to this use case.
Many thanks in advance,
Try this
SELECT STUFF((SELECT ',' + CAST(OrderNum AS VARCHAR(4))
FROM mytable AS s
WHERE s.PartNum = t.PartNum
FOR XML PATH('')), 1, 1, '') AS OrderNum
PartNum, SUM(Qty)
FROM mytable AS t
GROUP BY PartNum
This can be done by grouping on PartNum, sum the quantities with SUM() and concatenating strings using FOR XML PATH('') in a correlated subquery. Using FOR XML PATH('') to concatenate string is explained in this answer on SO.
DECLARE #t TABLE(OrderNum INT, PartNum INT, Qty INT);
INSERT INTO #t(OrderNum,PartNum,Qty)
VALUES(1,24,2),(2,25,10),(3,24,5),(4,24,10);
SELECT
OrderNum=STUFF((
SELECT
','+CAST(i.OrderNum AS VARCHAR)
FROM
#t AS i
WHERE
i.PartNum=o.PartNum
FOR XML
PATH(''), TYPE
).value('.[1]','VARCHAR(MAX)'),1,1,''),
o.PartNum,
Qty=SUM(o.Qty)
FROM
#t AS o
GROUP BY
o.PartNum;
Result:
OrderNum | PartNum | Qty
------------------------
1,3,4 | 24 | 17
2 | 25 | 10
SQL Server 2016 added the STRING_AGG function.
In your case, you could write
select STRING_ACC(OrderId,','),PartNum, Sum(Qty)
from MyTable
Group by PartNum
For earlier versions you'd have to use one of the techniques described by Aaron Bertrand in Grouped Concatenation in SQL Server. The fastest is to use a SQLCLR method. Next comes the FOR XML method posted by #GiorgosBetsos
DECLARE #t TABLE(OrderNum INT, PartNum INT, Qty INT)
INSERT INTO #t VALUES(1 , 24 , 2)
INSERT INTO #t VALUES(2 , 25 , 10)
INSERT INTO #t VALUES(3 , 24 , 5)
INSERT INTO #t VALUES(4 , 24 , 10)
SELECT OrderNum =
STUFF((SELECT ', ' + CONVERT(VARCHAR(50),OrderNum)
FROM #t b
WHERE b.PartNum = a.PartNum
FOR XML PATH('')), 1, 2, ''),
PartNum,
SUM(Qty) as Qty
FROM #t a
GROUP BY PartNum
Result
There are many ways to do this.
create table tablename (Name varchar(100), Rnk int)
Insert into tablename values
('Northshore', 1),
('F3', 2),
('Borderline', 3),
('Mattoon',3),
('Vinemane',5),
('Arizona',5),
('WestShore', 5),
('Schumburg', 5),
('Wilson',5)
--Method2
Select distinct
names= REPLACE(
(
Select a.Name as [data()]
From tablename A
Where A.Rnk = b.Rnk
Order by a.Name
FOR XML PATH ('') ), ' ', ',') ,Rnk
From tablename B Order by Rnk
OR
CREATE TABLE TestTable (ID INT, Col VARCHAR(4))
GO
INSERT INTO TestTable (ID, Col)
SELECT 1, 'A'
UNION ALL
SELECT 1, 'B'
UNION ALL
SELECT 1, 'C'
UNION ALL
SELECT 2, 'A'
UNION ALL
SELECT 2, 'B'
UNION ALL
SELECT 2, 'C'
UNION ALL
SELECT 2, 'D'
UNION ALL
SELECT 2, 'E'
GO
SELECT *
FROM TestTable
GO
-- Get CSV values
SELECT t.ID, STUFF(
(SELECT ',' + s.Col
FROM TestTable s
WHERE s.ID = t.ID
FOR XML PATH('')),1,1,'') AS CSV
FROM TestTable AS t
GROUP BY t.ID
GO
OR
CREATE TABLE #mable(mid INT, token nvarchar(16))
INSERT INTO #mable VALUES (0, 'foo')
INSERT INTO #mable VALUES(0, 'goo')
INSERT INTO #mable VALUES(1, 'hoo')
INSERT INTO #mable VALUES(1, 'moo')
SELECT m1.mid,
( SELECT m2.token + ','
FROM #mable m2
WHERE m2.mid = m1.mid
ORDER BY token
FOR XML PATH('') ) AS token
FROM #mable m1
GROUP BY m1.mid ;
Also, see this.
http://blog.sqlauthority.com/2009/11/25/sql-server-comma-separated-values-csv-from-table-column/

MSSQL Group concatenation adding columns as agroup by

Table Structure
Item, Group, Min Qty, Price
A, 1, 10, 1.00
A, 2, 10, 0.75
B, 1, 20, 0.90
C, 3, 5, 5.00
Sql query I am running currently which works for all the groups into 1 column, I'm trying to work out how I add a column for each group and only put in the values from that group only.
SELECT [Item],
STUFF((
SELECT ', ' + CAST([Min Qty] AS VARCHAR(MAX)) + ':' + CAST([Price] AS VARCHAR(MAX) + ';')
FROM [Table] WHERE ([Item No_] = Results.[Item] and [Minimum Qty] > '1')
FOR XML PATH(''),TYPE
).value('.','VARCHAR(MAX)')
,1,2,'') as Values
FROM [Table] Results
GROUP BY [Item]
Current output
Item, Values
A, 10:0.75; 10:1.00;
B, 20:0.90;
C, 5:5.00;
Required output
Item, Group 1, Group 2, Group 3
A, 10:1.00; 10:0.75;
B, 20:0.90;
C, 5:5.00;
Thank you for input
Edit from below
We don't know all the column names as they will be added by other users to the system, so needs to auto add the columns also should be grouping all entries for that group/item into 1 column as I'm trying to produce an output file.
Table information
Item, Group, Min Qty, Price
A, 1, 10, 1.00
A, 2, 10, 0.75
B, 1, 20, 0.90
C, 3, 5, 5.00
A, 1, 20, 0.50
Item, Group 1, Group 2, Group 3
A, 10:1.00;20:0.50; 10:0.75;
B, 20:0.90;
C, 5:5.00;
declare #t table (Item Varchar(2),Groups Varchar(2),MInqty varchar(10),Price Money)
insert into #t (Item,Groups,MInqty,Price)values
('A,','1,',10,1.00),
('A,','2,',10,0.75),
('B,','1,',20,0.90),
('C,','3,',5,5.00)
select Item,[1,] AS [Groups 1],[2,] AS [Groups 2],[3,] AS [Groups 3]
from (
select Item,Groups,MInqty +':'+ CAST(price AS VARCHAR) + ';' As GRP from #t)P
PIVOT (MIN(GRP) FOR GROUPS IN ([1,],[2,],[3,]))PVT
Remodified answer check
Select Item,[1] AS [Groups 1],[2] AS [Groups 2],[3] AS [Groups 3] from (
Select P.Item,MIN(R)G,P.Value from (
SELECT [Item],SUBSTRING(Groups,0,CHARINDEX(',',Groups))R,
STUFF((
SELECT ', ' + CAST([MInqty] AS VARCHAR(MAX)) + ':' + CAST([Price] AS VARCHAR(MAX) )+';'As Grp
FROM #t WHERE ([Item] = Results.[Item] and [MInqty] > '1')
FOR XML PATH(''),TYPE
).value('.','VARCHAR(MAX)')
,1,2,'') as Value
FROM #t Results
GROUP BY Groups,Item)P
GROUP BY P.Item,p.Value)PP
PIVOT (MIN(Value)FOR G IN ([1],[2],[3]) )PVT

How to use pivot in this table

Actual Table Structure
===================================
slno ParnetID ParnetName Promotion Marketer
1 SLM1010S SKR.RAJASHEGARAN 2 43640
2 40049 M.KANNAN 3 43640
3 40018 M.PRABU 6 43640
4 SLM1010S SKR.RAJASHEGARAN 2 43641
5 40042 M.KANNAN 3 43641
6 40011 M.PRABU 6 43641
i have my query :
WITH temp
AS (SELECT slno,
parentid,
parentname,
parentpromotionid,
marketerid,
(SELECT Count(*)
FROM parentmaster
WHERE t1.marketerid = marketerid
AND t1.parentid = parentid
AND t1.parentname = parentname
AND slno <= t1.slno) AS RowNum
FROM parentmaster AS t1)
SELECT marketerid,
[2],
[6],
[3]
FROM temp
PIVOT ( Min(parentid)
FOR parentpromotionid IN ([2],
[6],
[3]) ) AS t
But I want This Table Structure
MarketerID 2 6 3
43640 SLM1010S 40018 40049
43641 SLM1010S 40011 40042
This may help u..
select marketer,[2],[3],[6] from
(
select Marketer,Promotion,ParnetID
from parentmaster
) d pivot (min(ParnetID) for Promotion in ([2],[3],[6])) as pvt
try this,
Declare #t table(slno int,ParnetID varchar(50),ParnetName varchar(50),Promotion int,Marketer int)
insert into #t
select 1,'SLM1010S','SKR.RAJASHEGARAN', 2, 43640 union all
select 2,'40049','M.KANNAN', 3, 43640 union all
select 3,'40018', 'M.PRABU', 6, 43640 union all
select 4,'SLM1010S', 'SKR.RAJASHEGARAN', 2, 43641 union all
select 5,'40042', 'M.KANNAN', 3, 43641 union all
select 6,'40011', 'M.PRABU', 6, 43641
select distinct a.Marketer,b.ParnetID ,c.ParnetID,d.ParnetID from #t a
left join #t b on a.Marketer=b.Marketer and b.promotion=2
left join #t c on a.Marketer=c.Marketer and c.promotion=3
left join #t d on a.Marketer=d.Marketer and d.promotion=6
select Marketer, [0],[1],[2],[3],[4],[5],[6]
from (
select Marketer,Promotion,ParnetID
from #t
) d
pivot
(
MIN(ParnetID)
FOR Promotion
IN ([0],[1],[2],[3],[4],[5],[6])
) as TST

How to find the parent ID in a return table

I have following table:
ID ParentID
1 NULL
2 1
3 2
4 NULL
5 4
6 5
7 3
I want to find the first ID of a specific child ID.
Example: ID=7 and the result is 1
ID=6 and the result is 4
How to do it?
You need to do a bit of recursive CTE magic to solve this one.
Given the data in a table variable thusly:
declare #data table(id int, parentid int)
insert into #data
select 1, null
union select 2,1
union select 3,2
union select 4, null
union select 5,4
union select 6,5
union select 7,3
The following should do the trick:
;with recursiveTable as
(
select d.id, d.parentId, 0 as depth
from #data d
where d.id=6 -- Parameterize this
union all
select d.id, d.parentid, r.depth-1
from #data d
inner join recursiveTable r
on d.id = r.parentId
)
select top 1 id
from recursiveTable
order by depth
Plugging 6 in as above returns 4. Changing this to 7 returns 1 as requested.
Try this:
CREATE TABLE childpar(ID int,ParentID int)
INSERT INTO childpar
values(1,NULL),
(2, 1),
(3, 2),
(4, NULL),
(5, 4),
(6, 5),
(7, 3)
DECLARE #inpyID int
SET #inpyID=7
;WITH CTE1 as (
select * from childpar where id=#inpyID
union all
select c2.* from CTE1 c1 inner join childpar c2 on c1.ParentID = c2.ID
)
select top 1 id from CTE1 order by id asc

Resources