Pivot and merge columns in a SQL Server stored procedure - sql

I have a table with the following columns
Col1(bigint) | Col2(datetime)| Col3(nvarchar(100))| Col4(xml)
The 4th column is a xml which looks like following, it may contain any number of fields
<Fields>
<Field1>10</Field1>
<Field2>11</Field2>
<Field3>10</Field3>
<Field4>11</Field4>
</Fields>
I need to query multiple rows from the table. I am creating a stored procedure which gets 2 parameters
Parameter1: #query (type of xml)
<Rowset>
<Row>
<Col1>20140510123205321</Col1>
<Col2>2014-05-14T13:01:03.1426856+05:30</Col2>
<Col3>Source1</Col3>
</Row>
<Row>
<Col1>20140510123205322</Col1>
<Col2>2014-05-14T13:01:03.1426856+05:30</Col2>
<Col3>Source2</Col3>
</Row>
</Rowset>
Parameter2: #Fields (type of string comma separated)
Field1,Field2,Field3,Field4
I am expecting a output in the following format
Col1 | Col2 | Col3 | Field1 | Field2 | Field3 | Field4
-----------------------------------------------------------------
2014051092|2014-05-14|Source1| 10 | 21 | 12 | 43
2014051093|2014-05-14|Source1| 11 | 22 | 23 | 53
I have created a table from the first parameter in the stored procedure.
INSERT INTO #TempPricing
(
Col1,
Col2,
Col3
)
SELECT
Col1,
Col2,
Col3
FROM OPENXML(#handle, '/Rowset/Row', 2)
WITH
(
Col1 bigint,
Col2 datetime,
Col3 varchar(50)
)
How can I pivot the second parameter after splitting, merge it with the table above and fetch the data?
EDIT
The output can also be the following format
Col1 | Col2 | Col3 | Fields
--------------------------------------
2014051092|2014-05-14|Source1|<Fields><Field1>10</Field1><Field1>11</Field1></Fields>
2014051093|2014-05-14|Source1|<Fields><Field1>20</Field1><Field1>21</Field1></Fields>
so any of the above formats will work

Lets try the below script it may help you.
declare #String varchar(max), #Result varchar(max)
select #String= COALESCE(#String+',', '')+ QUOTENAME(Col1)
FROM (
select distinct Col1 from TableA A Inner Join dbo.Tab C VS on A.AttributeID =VS.AttributeId)AS B
set #Result ='
with pivotData AS
(
select distinct P.Col1, PA.col1,A.Col1,PA.col2 from Tab3 PA join Attribute A
on A.AttributeID=PA.AttributeID join Product P on P.ProductId=PA.ProductId
)
select Col1, Col2,
'+#String+' from pivotData
pivot(
Max(col2) for
Col1in('+#String+')
)as PR order by col3 desc'
exec (#Result)

Related

How to select only a few columns of a table

I have a Table which contain around 1000 columns. When I use
Select *
from Table
Its Return entire record of the table. But I just want only limited column of the record.
col1 | col2 | col3 | col4 | col 5 | ......................... | col1000 |
| | | | | ------------------------- | |
| | | | | ------------------------- | |
| | | | | ------------------------- | |
| | | | | ------------------------- | |
------------------------------------------------------------------------------------------------------
I just need col5 to col1000 record data only.
you have to write all the columns that you need in select
select col5, col6, ......... ,col1000 from table
there is no shot-cut way with out it and select * means all the columns of your table
If you really want to do without typing each column name, one way is using dynamic query.
For example in SQL Server you can write the dynamic query like following.
DECLARE #selstmt AS NVARCHAR(max);
SET #selstmt = 'select ' + Stuff((SELECT ', ' + Quotename(NAME) FROM
( SELECT c.NAME FROM
sys.columns c JOIN sys.tables t ON c.object_id = t.object_id
WHERE t.NAME = 'yourtablename'
AND c.NAME NOT IN('col1', 'col2', 'col3', 'col4')
)t FOR
xml path(''), type).value('.',
'NVARCHAR(MAX)'), 1, 1, '');
SET #selstmt = #selstmt + ' from yourtable'
EXEC sp_executesql #selstmt
Specify column names that you want to select instead of using * operator.
SELECT * will return all columns for table
SELECT col5, col6, col7,...., col1000 (col5 upto col1000) will return only specified columns of the table
There actually is one easy way in SSMS.
SELECT * FROM TableA
Select this text, press CNTRL SHIFT Q
Then you have all the columns and can easily remove a few.
You have to write all columns.
SELECT col1, col2, col3 from table;
But...
Tested on MySQL
Since you have too many columns, you can run a query to select the desired columns from the table:
SELECT GROUP_CONCAT(COLUMN_NAME SEPARATOR ', ')
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'schemaName' AND TABLE_NAME = 'table'
AND COLUMN_NAME IN ('col1', 'col2', 'col3');
# or to filter the columns
# AND COLUMN_NAME NOT IN ('col1', 'col2', 'col3');
And build the query with the resultant columns.
GROUP_CONCAT outputs the values in a single row separated with a ,, so you can use them to query the table directly
SELECT
col1,col2,col3
FROM
table_name

Import Unpivot results to new Table and match on Key

I currently have a few unpivot queries that yeilds about 2000 rows each. I need to take the results of those queires, and put in a new table to match on a key.
Query Example:
Select DeviceSlot
FROM tbl1
unpivot(
DeviceSlot
For col in(
col1,
col2,
col3,
)
)AS Unpivot
Now I need to match the results from the query, and insert it into a new table with about 20,000 rows.
Pseudo-Code for this:
Insert Into tbl2(DeviceSlot)
Select DeviceSlot
FROM tbl1
unpivot(
DeviceSlot
For col in(
col1,
col2,
col3
)
)AS Unpivot2
Where tbl1.key = tbl2.key
I've been pretty confused on how to do this, and I apologize if it is not clear.
I also have another unpivot query doing the same thing for different columns.
Not sure what you are asking for. While unpivoting to "normalize" data typically the wanted "key" is derived during the unpivot, for example, below the id column of the original table is repeated in the un-pivoted data to represent a foreign key for some new table.
SQL Fiddle
MS SQL Server 2014 Schema Setup:
CREATE TABLE Table1
([id] int, [col1] varchar(2), [col2] varchar(2), [col3] varchar(2))
;
INSERT INTO Table1
([id], [col1], [col2], [col3])
VALUES
(1, 'a', 'b', 'c'),
(2, 'aa', 'bb', 'cc')
;
Query 1:
select id as table1_fk, colheading, colvalue
from (
select * from table1
) t
unpivot (
colvalue for colheading in (col1, col2, col3)
) u
Results:
| table1_fk | colheading | colvalue |
|-----------|------------|----------|
| 1 | col1 | a |
| 1 | col2 | b |
| 1 | col3 | c |
| 2 | col1 | aa |
| 2 | col2 | bb |
| 2 | col3 | cc |

get all row values in single record

i have a table in which i want to get column values into single record separated by comma
using the stuff() with select ... for xml path ('') method of string concatenation.
select col1, col2, col3 = stuff(
(
select ','+i.col3
from t as i
where i.col1 = t.col1
for xml path (''), type).value('.','nvarchar(max)')
,1,1,'')
from t
group by col1, col2
rextester demo: http://rextester.com/QXH88855
returns:
+------+------+-------------+
| col1 | col2 | col3 |
+------+------+-------------+
| 1 | roy | a,f,g,h |
| 2 | sam | h,k,l |
| 3 | joe | q,w,e,r,t,y |
+------+------+-------------+
If SQL Server 2017 or Vnext or SQL Azure you can use string_agg
SELECT col1, col2, STRING_AGG(col3, ',') from yourtable
GROUP BY col1, col2
Formatting the output really should be done in the program that receives the data. You can export from SQL Server Management studio to csv by selecting "output to file" on the toolbar and configuring comma delimited output.
If you really need to combine the columns into a comma separated single value:
SELECT CAST(col1 AS NVARCHAR(100)) + N',' + CAST(col2 AS NVARCHAR(100)) + N',' + CAST(col3 AS NVARCHAR(100))
FROM table

Group Concat in Redshift

I have a table like this:
| Col1 | Col2 |
|:-----------|------------:|
| 1 | a;b; |
| 1 | b;c; |
| 2 | c;d; |
| 2 | d;e; |
I want the result to be some thing like this.
| Col1 | Col2 |
|:-----------|------------:|
| 1 | a;b;c;|
| 2 | c;d;e;|
Is there some way to write a set function which adds unique values in a column into an array and then displays them. I am using the Redshift Database which mostly uses postgresql with the following difference:
Unsupported PostgreSQL Functions
Have a look at Redshift's listagg() function which is similar to MySQL's group_concat. You would need to split the items first and then use listagg() to give you a list of values. Do take note, though, that, as the documentation states:
LISTAGG does not support DISTINCT expressions
(Edit: As of 11th October 2018, DISTINCT is now supported. See the docs.)
So will have to take care of that yourself. Assuming you have the following table set up:
create table _test (col1 int, col2 varchar(10));
insert into _test values (1, 'a;b;'), (1, 'b;c;'), (2, 'c;d;'), (2, 'd;e;');
Fixed number of items in Col2
Perform as many split_part() operations as there are items in Col2:
select
col1
, listagg(col2, ';') within group (order by col2)
from (
select col1, split_part(col2, ';', 1) as col2 from _test
union select col1, split_part(col2, ';', 2) as col2 from _test
)
group by col1
;
Varying number of items in Col2
You would need a helper here. If there are more rows in the table than items in Col2, a workaround with row_number() could work (but is expensive for large tables):
with _helper as (
select
(row_number() over())::int as part_number
from
_test
),
_values as (
select distinct
col1
, split_part(col2, ';', part_number) as col2
from
_test, _helper
where
length(split_part(col2, ';', part_number)) > 0
)
select
col1
, listagg(col2, ';') within group (order by col2) as col2
from
_values
group by
col1
;

Dynamically Join Enum Table with other Table

I have the following table. These are not the real tables but the concept is here.
Table1
----------------------------------
FieldID|EnumeratedValue|TextValue
----------------------------------
Col1 |1 |Hey
----------------------------------
Col1 |2 |Test
----------------------------------
Col1 |3 |George
----------------------------------
Col2 |1 |Random
----------------------------------
Col2 |2 |Wesley
----------------------------------
Col3 |1 |Tompson
----------------------------------
Col3 |2 |Oooo
----------------------------------
Table2
----------------------------------
Col1 |Col2 |Col3
----------------------------------
1 |2 |1
----------------------------------
2 |1 |1
----------------------------------
3 |1 |2
----------------------------------
The desired result would be a view
----------------------------------
Col1 |Col2 |Col3
----------------------------------
Hey |Wesley |Tompson
----------------------------------
Test |Random |Tompson
----------------------------------
George |Random |Oooo
----------------------------------
So you could write something like
SELECT col1.TextValue,col2.TextValue,col3.TextValue
FROM Table2 t2,
(SELECT * FROM Table1 WHERE FieldID = 'Col1') col1,
(SELECT * FROM Table1 WHERE FieldID = 'Col2') col2,
(SELECT * FROM Table1 WHERE FieldID = 'Col3') col3
WHERE t2.col1 = col1.EnumeratedValue and t2.col2 = col2.EnumeratedValue and t2.col3 = col3.EnumeratedValue
The problem is that in our real tables there ~20 of these columns per table. I would like to find a simpler means of writing this code. I do not want to hard code 20 joins for each view that I am making. Let me know if there are any alternatives.
As with any variable join solution, a dynamic pivot is really the better way to look at this. It's not as performance as a hard-coded, non-pivot solution, but this will involve the least development hours, and can be adapted to generate view definitions rather than made into a table-valued function (which could be fed a table name and possibly column names to pivot and join)
First, the demo data setup:
if object_id('dbo.test_enum') is not null drop table dbo.test_enum
create table dbo.test_enum (
FieldID sysname
, EnumeratedValue int not null
, TextValue sysname
, primary key (FieldID, EnumeratedValue)
)
INSERT INTO dbo.test_enum
(FieldID, EnumeratedValue, TextValue)
VALUES
('Col1', 1, 'Hey'),
('Col1', 2, 'Test'),
('Col1', 3, 'George'),
('Col2', 1, 'Random'),
('Col2', 2, 'Wesley'),
('Col3', 1, 'Tompson'),
('Col3', 2, 'Oooo')
;
if object_id('dbo.test_table') is not null drop table dbo.test_table
create table test_table (
id int primary key identity(1,1)
, Col1 int not null
, Col2 int not null
, Col3 int not null
);
INSERT INTO dbo.test_table
(Col1, Col2, Col3)
VALUES
(1,2,1),
(2,1,1),
(3,1,2)
Next is the dynamic selection part:
declare #cols nvarchar(max) = (select stuff((
select ',' + quotename(c.name)
from sys.objects o
inner join sys.columns c on c.object_id = o.object_id
where o.name = 'test_table'
and c.name in ('Col1', 'Col2', 'Col3')
for xml path ('')
),1,1,''))
declare #SQL nvarchar(max) = N'
select p.id, ' + #cols + '
from (
select unp.id, unp.FieldID, e.TextValue
from dbo.test_table
unpivot (
EnumeratedValues FOR FieldID IN (' + #cols + ')
) unp
inner join dbo.test_enum e on e.EnumeratedValue = unp.EnumeratedValues
and e.FieldID = unp.FieldID
) z
pivot (
min(z.TextValue) FOR z.FieldID IN (' + #cols + ')
) p
'
exec sp_executesql #sql
On its own, this will return the result set desired in the question, but can be adapted to return view definitions of any given table/column data set, and then further adapted per table/view.