Pivot SQL Select Result to Desired Output - sql

I am trying to convert a SQL result to desired output for analytic report.
Select statement and output is as follows:
SELECT QID, [Text], [Value] FROM TableA
OUTPUT SINGLE Question(QID)
OUTPUT MULTIPLE Question(QID)
Desired output result needed for report is as follows:
Can we achieve this result using Pivot table or similar way rather than using While and Cursor loop?
Thanks and regards,
Viju

The Above query will not work using PIVOT id qid values are same for all the records as we have to use any aggregate function in pivot
check the below query for reference
DECLARE #TAB TABLE (QID INT,[TEXT] VARCHAR(255),VALUE VARCHAR(255))
INSERT INTO #TAB VALUES (1,'WEAG','SDARG'),(1,'NUM','1'),(1,'ID','1')
,(2,'WEAG','RSTHGEST'),(2,'NUM','2'),(2,'ID','2')
,(3,'WEAG','SREVFGWR'),(3,'NUM','3'),(3,'ID','3')
SELECT * FROM #TAB
SELECT * FROM
(
SELECT T.QID,t.[TEXT],T.VALUE
FROM #TAB T
)A
PIVOT (MAX(VALUE) FOR [TEXT] IN (WEAG,ID) ) AS P
OUTPUT
QID WEAG ID
1 SDARG 1
2 RSTHGEST 2
3 SREVFGWR 3

Related

Efficient way to merge alternating values from two columns into one column in SQL Server

I have two columns in a table. I want to merge them into a single column, but the merge should be done taking alternate characters from each columns.
For example:
Column A --> value (1,2,3)
Column B --> value (A,B,C)
Required result - (1,A,2,B,3,C)
It should be done without loops.
You need to make use of the UNION and get a little creative with how you choose to alternate. My solution ended up looking like this.
SELECT ColumnA
FROM Table
WHERE ColumnA%2=1
UNION
SELECT ColumnB
FROM TABLE
WHERE ColumnA%2=0
If you have an ID/PK column that could just as easily be used, I just didn't want to assume anything about your table.
EDIT:
If your table contains duplicates that you wish to keep, use UNION ALL instead of UNION
Try This;
SELECT [value]
FROM [Table]
UNPIVOT
(
[value] FOR [Column] IN ([Column_A], [Column_B])
) UNPVT
If you have SQL 2016 or higher you can use:
SELECT QUOTENAME(STRING_AGG (cast(a as varchar(1)) + ',' + b, ','), '()')
FROM test;
In older versions, depending on how much data you have in your tables you can also try:
SELECT QUOTENAME(STUFF(
(SELECT ',' + cast(a as varchar(1)) + ',' + b
FROM test
FOR XML PATH('')), 1, 1,''), '()')
Here you can try a sample
http://sqlfiddle.com/#!18/6c9af/5
with data as (
select *, row_number() over order by colA) as rn
from t
)
select rn,
case rn % 2 when 1 then colA else colB end as alternating
from data;
The following SQL uses undocumented aggregate concatenation technique. This is described in Inside Microsoft SQL Server 2008 T-SQL Programming on page 33.
declare #x varchar(max) = '';
declare #t table (a varchar(10), b varchar(10));
insert into #t values (1,'A'), (2,'B'),(3,'C');
select #x = #x + a + ',' + b + ','
from #t;
select '(' + LEFT(#x, LEN(#x) - 1) + ')';

Get unique values using STRING_AGG in SQL Server

The following query returns the results shown below:
SELECT
ProjectID, newID.value
FROM
[dbo].[Data] WITH(NOLOCK)
CROSS APPLY
STRING_SPLIT([bID],';') AS newID
WHERE
newID.value IN ('O95833', 'Q96NY7-2')
Results:
ProjectID value
---------------------
2 Q96NY7-2
2 O95833
2 O95833
2 Q96NY7-2
2 O95833
2 Q96NY7-2
4 Q96NY7-2
4 Q96NY7-2
Using the newly added STRING_AGG function (in SQL Server 2017) as it is shown in the following query I am able to get the result-set below.
SELECT
ProjectID,
STRING_AGG( newID.value, ',') WITHIN GROUP (ORDER BY newID.value) AS
NewField
FROM
[dbo].[Data] WITH(NOLOCK)
CROSS APPLY
STRING_SPLIT([bID],';') AS newID
WHERE
newID.value IN ('O95833', 'Q96NY7-2')
GROUP BY
ProjectID
ORDER BY
ProjectID
Results:
ProjectID NewField
-------------------------------------------------------------
2 O95833,O95833,O95833,Q96NY7-2,Q96NY7-2,Q96NY7-2
4 Q96NY7-2,Q96NY7-2
I would like my final output to have only unique elements as below:
ProjectID NewField
-------------------------------
2 O95833, Q96NY7-2
4 Q96NY7-2
Any suggestions about how to get this result? Please feel free to refine/redesign from scratch my query if needed.
Use the DISTINCT keyword in a subquery to remove duplicates before combining the results: SQL Fiddle
SELECT
ProjectID
,STRING_AGG(value, ',') WITHIN GROUP (ORDER BY value) AS
NewField
from (
select distinct ProjectId, newId.value
FROM [dbo].[Data] WITH(NOLOCK)
CROSS APPLY STRING_SPLIT([bID],';') AS newID
WHERE newID.value IN ( 'O95833' , 'Q96NY7-2' )
) x
GROUP BY ProjectID
ORDER BY ProjectID
You can use distinct in the subquery used for the apply:
SELECT d.ProjectID,
STRING_AGG( newID.value, ',') WITHIN GROUP (ORDER BY newID.value) AS
NewField
FROM [dbo].[Data] d CROSS APPLY
(select distinct value
from STRING_SPLIT(d.[bID], ';') AS newID
) newID
WHERE newID.value IN ( 'O95833' , 'Q96NY7-2' )
group by projectid;
This is a function that I wrote that answers the OP Title:
Improvements welcome!
CREATE OR ALTER FUNCTION [dbo].[fn_DistinctWords]
(
#String NVARCHAR(MAX)
)
RETURNS NVARCHAR(MAX)
WITH SCHEMABINDING
AS
BEGIN
DECLARE #Result NVARCHAR(MAX);
WITH MY_CTE AS ( SELECT Distinct(value) FROM STRING_SPLIT(#String, ' ') )
SELECT #Result = STRING_AGG(value, ' ') FROM MY_CTE
RETURN #Result
END
GO
Use like:
SELECT dbo.fn_DistinctWords('One Two Three Two One');
As #SeanLange pointed out in the comments, this is a terrible way to pull out the data, but if you had to, just make it 2 separate queries as follows:
SELECT
ProjectID
,STRING_AGG( val, ',') WITHIN GROUP (ORDER BY val) AS NewField
FROM
(
SELECT DISTINCT
ProjectID
,newID.value AS val
FROM
[dbo].[Data] WITH(NOLOCK)
CROSS APPLY STRING_SPLIT([bID],';') AS newID
WHERE
newID.value IN ('O95833' , 'Q96NY7-2')
) t
GROUP BY
ProjectID
That should do it.
Another possibility to get unique strings from STRING_AGG would be to perform these three steps after fetching the comma separated string:
Split the string (STRING_SPLIT)
Select DISTINCT from the splits
Apply STRING_AGG again to a select with a group on a single key
Example:
(select STRING_AGG(CAST(value as VARCHAR(MAX)), ',')
from (SELECT distinct 1 single_key, value
FROM STRING_SPLIT(STRING_AGG(CAST(customer_division as VARCHAR(MAX)), ','), ','))
q group by single_key) as customer_division
Here is my improvement on #ttugates to make it more generic:
CREATE OR ALTER FUNCTION [dbo].[fn_DistinctList]
(
#String NVARCHAR(MAX),
#Delimiter char(1)
)
RETURNS NVARCHAR(MAX)
WITH SCHEMABINDING
AS
BEGIN
DECLARE #Result NVARCHAR(MAX);
WITH MY_CTE AS ( SELECT Distinct(value) FROM STRING_SPLIT(#String,
#Delimiter) )
SELECT #Result = STRING_AGG(value, #Delimiter) FROM MY_CTE
RETURN #Result
END
You can make a distinct view of the table, that holds the aggregate values, that is even simpler:
Create Table Test (field1 varchar(1), field2 varchar(1));
go
Create View DistinctTest as (Select distinct field1, field2 from test group by field1,field2);
go
insert into Test Select 'A', '1';
insert into Test Select 'A', '2';
insert into Test Select 'A', '2';
insert into Test Select 'A', '2';
insert into Test Select 'D', '1';
insert into Test Select 'D', '1';
select string_agg(field1, ',') from Test where field2 = '1'; /* duplicates: A,D,D */;
select string_agg(field1, ',') from DistinctTest where field2 = '1'; /* no duplicates: A,D */;
Oracle (since version 19c) suports listagg (DISTINCT ..., but Microsoft SQL Server not probably.

Get a specific string

It's my data and every ThroughRouteSid record has the same pattern.
six number and five comma. then I just want to get three and five
number into two record to template Table and get the same Count()
value to these two record.
For example: First record in the picture.
ThroughRouteSid(3730,2428,2428,3935,3935,3938,) Count(32).
I want a result like this:
2428 32 3935 32
I get What number I want.become two record and both have same Count value into template table
you can use XML to get your result, please refer below sample code -
create table #t1( ThroughRouteSid varchar(500) , Cnt int)
insert into #t1
select '3730,2428,2428,3935,3935,3938,' , len('3730,2428,2428,3935,3935,3938,')
union all select '1111,2222,3333,4444,5555,6666,' , len('1111,2222,3333,4444,5555,6666,')
select cast( '<xml><td>' + REPLACE( SUBSTRING(ThroughRouteSid ,1 , len(ThroughRouteSid)-1),',','</td><td>') + '</td></xml>' as xml) XmlData , Cnt
into #t2 from #t1
select XmlData.value('(xml/td)[3]' ,'int' ), Cnt ,XmlData.value('(xml/td)[5]' ,'int' ), Cnt
from #t2
First create the function referring How to Split a string by delimited char in SQL Server. Then try Querying the following
select (SELECT CONVERT(varchar,splitdata) + ' '+ Convert(varchar, [Count])+' ' FROM (select splitdata, ROW_NUMBER() over (ORDER BY (SELECT 100)) row_no
from [dbo].[fnSplitString](ThroughRouteSid,',')
where splitdata != '') as temp where row_no in (2,5)
for xml path('')) as col1 from [yourtable]
If you are using SQL Server 2016 you can do something like this:
create table #temp (ThroughRouteSid varchar(1024),[Count] int)
insert into #temp values
('3730,2428,2428,3935,3935,3938,',32),
('730,428,428,335,935,938,',28)
select
spt.value,
t.[Count]
from #temp t
cross apply (
select value from STRING_SPLIT(t.ThroughRouteSid,',') where LEN(value) > 0
)spt

How can I repeat a value from one column according to a value from another column?

I have data like below - one column with a value, the second with a count:
Name Count
----- -----
John 2
Smith 3
I want an output like below - each row consisting of the value in the first column repeated n times, where n is the value of the second column:
John,John
Smith,Smith,Smith
Is there a built-in Oracle function or SQL query that could be used to achieve this? If PL/SQL is required, that would also be helpful.
Looks to me that RPAD can do it
This is NOT TESTED as I don't have an Oracle db to hand
RPAD("", (LENGTH(name)+1)*count -1, name||',')
(rpad a blank string with count copies of the string, by requiring the result to be (length(name)+1)*count -1 long. The -1 is to remove the trailing comma )
Hat tip to OracleUser for the appending-the comma bit.
I'm not familiar with oracle, but if you know how to use recursion in oracle then you might be able to convert my T-SQL code to what works for you...
Step 1. Create test table containing your sample data:
CREATE TABLE #t ([Name] VARCHAR(20), [Count] INT);
INSERT INTO #t VALUES('John',2)
INSERT INTO #t VALUES('Smith',3)
SELECT * FROM #t
This gives the following output:
Name Count
-------------------- -----------
John 2
Smith 3
Step 2. Recursive Query to get desired output:
DECLARE #Separator VARCHAR(3); SET #Separator = ','
;WITH myCTE AS (
SELECT 1 AS [Count], [Name], [Count] AS RequiredCount, [Name] AS Result FROM #t
UNION ALL
SELECT [Count] + 1, [Name], RequiredCount, CAST(Result + #Separator + [Name] AS VARCHAR(20)) FROM myCTE WHERE RequiredCount > [Count]
)
SELECT [Count], [Name], Result FROM myCTE WHERE [Count] = RequiredCount
This gives the following output:
Count Name Result
----------- -------------------- --------------------
3 Smith Smith,Smith,Smith
2 John John,John
If this doesn't make sense to you, post a comment... I'll try and explain. I'm sure someone familiar with PL/SQL can help you convert this to meet your needs.
All the best!

Comma separated values in one column - SQL SERVER

Customer Table
--------------
ID Name
1 James
2 Peter
Order Table
---------------
OrderId CustId
100 1
101 1
102 2
How can I write a query that returns something like this
ID,Name,ListofOrders
1,James,"100,101"
2,Peter,"102"
In Sybase I had a function called LIST which I could use but I dont find a similar function in SQL SERVER
Please try:
select ID, [Name],
(select OrderID+',' from OrderTable where CustID=ID
group by OrderID for xml path('')) AS ListOfOrders
From CustomerTable
Create a User Defined Function as shown below
CREATE FUNCTION [dbo].[CommaSeperatedOrderIDs](#CustId INT) returns varchar(Max)
AS
BEGIN
DECLARE #CommaSeperatedValues VARCHAR(MAX)
SELECT #CommaSeperatedValues = COALESCE(#CommaSeperatedValues+',' , '') + OrderID
FROM OrderTable WHERE CustId = #CustId
RETURN #CommaSeperatedValues
END
And then,
select ID, [Name], ([dbo].[CommaSeperatedOrderIDs](ID)) AS ListofOrders
From CustomerTable
Adding full details from Sheikh Haris' link.
Given this table:
To get output like:
Use the following SQL:
SELECT field1,
Substring(convert(varchar(100),
(
SELECT (', ' + field2)
FROM #test t2
WHERE t1.field1 = t2.field1
ORDER BY field1, field2
FOR XML PATH( '' )
)), 3, 1000 )
FROM #test t1
GROUP BY field1
I added a convert function to the substring so that the WIDEMEMO field would be displayed (SQL Server)
A very simple and handy solution given on the link below.
http://tejasnshah.wordpress.com/2009/02/28/sql-server-get-column-values-as-comma-seperated-list-using-xml-path-instead-of-udfs-using-sql-coalesce/
The SQL query written on that link is in an image ...so i couldn't copy it here.