Can a Pivot table be used with a unknown number of columns? - sql

If I have a team table with a unknown amount of members, is there a way to make the pivot query dynamic?
create table #t (
team varchar (20), member varchar (20)
)
insert into #t values ('ERP', 'Jack')
insert into #t values ('ERP', 'John')
insert into #t values ('ERP', 'Mary')
insert into #t values ('ERP', 'Tim')
insert into #t values ('CRM', 'Robert')
insert into #t values ('CRM', 'Diana')
select * from #t
select team, [1] as teamMember1, /* 1st select */
[2] as teamMember2, [3] as teamMember3
from
(select team , member, row_number () /* 3rd select */
over (partition by team order by team) as rownum
from #t) a
pivot (max(member) for rownum in ([1], [2], [3])) as pvt
drop table #t

Why yes, yes there is. Here's a script I cooked up years ago for a similar problem that was ultimately solved by giving the user Excel and washing my hands of it. I apologize it's not configured with your example data, but hopefully it's easy to follow.
Hope that helps,
John
--------------START QUERY--------------
-- Example Table
CREATE TABLE #glbTestTable
(
ProviderID INT,
Total INT,
PaymentDate SMALLDATETIME
)
--So the dates insert properly
SET DATEFORMAT dmy
-- Populate Example Table
INSERT INTO #glbTestTable VALUES (232, 12200, '12/01/09')
INSERT INTO #glbTestTable VALUES (456, 10200, '12/01/09')
INSERT INTO #glbTestTable VALUES (563, 11899, '02/03/09')
INSERT INTO #glbTestTable VALUES (221, 5239, '13/04/09')
INSERT INTO #glbTestTable VALUES (987, 7899, '02/03/09')
INSERT INTO #glbTestTable VALUES (1, 1234, '02/08/09')
INSERT INTO #glbTestTable VALUES (2, 4321, '02/07/09')
INSERT INTO #glbTestTable VALUES (3, 5555, '02/06/09')
-- Raw Output
SELECT *
FROM #glbTestTable
-- Build Query for Pivot --
DECLARE #pvtColumns VARCHAR(MAX)
SET #pvtColumns = ''
-- Grab up to the first 1023 "Columns" that we want to use in Pivot Table.
-- Tables can only have 1024 columns at a maximum
SELECT TOP 1023 #pvtColumns = #pvtColumns + '[' + CONVERT(VARCHAR, PaymentDate, 103) + '], '
FROM (SELECT DISTINCT PaymentDate FROM #glbTestTable) t_distFP
-- Create PivotTable Query
DECLARE #myQuery VARCHAR(MAX)
SET #myQuery = '
SELECT ProviderID, ' + LEFT(#pvtColumns, LEN(#pvtColumns) - 1) + '
FROM (SELECT ProviderID, PaymentDate, Total
FROM #glbTestTable) AS SourceTable
PIVOT
(
SUM(Total)
FOR PaymentDate IN (' + LEFT(#pvtColumns, LEN(#pvtColumns) - 1) + ')
) AS PivotTable'
-- Run the Pivot Query
EXEC(#myQuery)
-- Cleanup
DROP TABLE #glbTestTable
---------------END QUERY---------------

Related

SQL output of dynamic queries should be in csv

Referring to the code in one of the links found in the internet - that executes sql command stored in DB column
I was trying to get similar output in the form of csv - like query and data along with headers in one result.
Like if we have this data in sql server 2014
CREATE TABLE Table1 (empid INT,
name VARCHAR(1000),
sal INT,
DOB DATETIME,
OrgID INT);
CREATE TABLE Table2 (OrgID INT,
Orgname VARCHAR(1000));
CREATE TABLE Table3 (dummy INT);
INSERT INTO Table1
VALUES (100, 'tst', '10000', '1990-11-20', 1);
INSERT INTO Table1
VALUES (101, 'tst', '10000', '1990-11-20', 1);
INSERT INTO Table1
VALUES (102, 'tst', '10000', '1990-11-20', 2);
INSERT INTO Table2
VALUES (1, 'org1');
INSERT INTO Table2
VALUES (2, 'org2');
CREATE TABLE #List (Command VARCHAR(MAX),
OrderBy INT IDENTITY(1, 1));
INSERT INTO #List
VALUES ('SELECT * FROM Table1'),
('SELECT * FROM Table2'),
('SELECT * FROM Table3');
DECLARE #sqlcmd VARCHAR(MAX);
SELECT #sqlcmd = STUFF(
(SELECT ';' + CHAR(10) + Command + CHAR(10)FROM #List ORDER BY [OrderBy] FOR XML PATH('')), 1, 1, '');
EXEC (#sqlcmd);
Now when i execute the sql's in #List, the output should result with comma seperated.
As they are no records in table3, it should not output in final result.
Am expected the the result all in one column seperated by comma - screenshot attached.
OUTPUT
SELECT FROM Table1, empid,name,sal,DOB,OrgID
SELECT FROM Table1, 100,tst,10000,1990-11-20 00:00:00.000,1
SELECT FROM Table1, 101,tst,10000,1990-11-20 00:00:00.000,1
SELECT FROM Table1, 102,tst,10000,1990-11-20 00:00:00.000,2
select from Table2,OrgID, Orgname
select from Table2,1,org1
select * from Table2,2,org2

Sql Server While Loop with Changing Condition

I have a User Table in my database that contains two fields
user_id
manager_id
I am trying to construct a query to list all of the manager_ids that are associated with a user_id in a hierarchical structure.
So if i give a user_id, i will get that users manager, followed by that persons manager all the way to the very top.
So far i have tried but it doesnt give what i need:
WITH cte(user_id, manager_id) as (
SELECT user_id, manager_id
FROM user
WHERE manager_id=#userid
UNION ALL
SELECT u.user_id, u.manager_id,
FROM user u
INNER JOIN cte c on e.manager_id = c.employee_id
)
INSERT INTO #tbl (manager_id)
select user_id, manager_id from cte;
If anyone can point me in the right direction that would be great.
I thought about a While loop but this may not be very efficient and im not too sure how to implement that.
OP asked for a while loop, and while (ha, pun) this may not be the best way... Ask and you shall receive. (:
Here is sample data I created (in the future, please provide this):
CREATE TABLE #temp (userID int, managerID int)
INSERT INTO #temp VALUES (1, 3)
INSERT INTO #temp VALUES (2, 3)
INSERT INTO #temp VALUES (3, 7)
INSERT INTO #temp VALUES (4, 6)
INSERT INTO #temp VALUES (5, 7)
INSERT INTO #temp VALUES (6, 9)
INSERT INTO #temp VALUES (7, 10)
INSERT INTO #temp VALUES (8, 10)
INSERT INTO #temp VALUES (9, 10)
INSERT INTO #temp VALUES (10, 12)
INSERT INTO #temp VALUES (11, 12)
INSERT INTO #temp VALUES (12, NULL)
While Loop:
CREATE TABLE #results (userID INT, managerID INT)
DECLARE #currentUser INT = 1 -- Would be your parameter!
DECLARE #maxUser INT
DECLARE #userManager INT
SELECT #maxUser = MAX(userID) FROM #temp
WHILE #currentUser <= #maxUser
BEGIN
SELECT #userManager = managerID FROM #temp WHERE userID = #currentUser
INSERT INTO #results VALUES (#currentUser, #userManager)
SET #currentUser = #userManager
END
SELECT * FROM #results
DROP TABLE #temp
DROP TABLE #results
Get rid of this column list in your CTE declaration that has nothing to do with the columns you are actually selecting in the CTE:
WITH cte(employee_id, name, reports_to_emp_no, job_number) as (
Just make it this:
WITH cte as (
I recommend recursive solution:
WITH Parent AS
(
SELECT * FROM user WHERE user_id=#userId
UNION ALL
SELECT T.* FROM user T
JOIN Parent P ON P.manager_id=T.user_id
)
SELECT * FROM Parent
To see demo, run following:
SELECT * INTO #t FROM (VALUES (1,NULL),(2,1),(3,2),(4,1)) T(user_id,manager_id);
DECLARE #userId int = 3;
WITH Parent AS
(
SELECT * FROM #t WHERE user_id=#userId
UNION ALL
SELECT T.* FROM #t T
JOIN Parent P ON P.manager_id=T.user_id
)
SELECT * FROM Parent

generate custom id with conditions while inserting records to sql server

How can I generate custom ids upon inserting records base on a condition? For example, I have a donor database program which accepts donations from non-alumni and alumni. Custom id from an alumni should start on A00001 and for non-alumni is N00001. And to note, the record that will be inserted will come from another table which records came from an excel file.
Any help or clarifications would be appreciated.
Something like this perhaps?
DECLARE #Table TABLE ( ID VARCHAR(20) );
-- Non alumni
INSERT INTO #Table
( ID )
VALUES ( 'N00011' )
INSERT INTO #Table
( ID )
VALUES ( 'N00012' )
INSERT INTO #Table
( ID )
VALUES ( 'N00013' )
-- Alumni
INSERT INTO #Table
( ID )
VALUES ( 'A00011' )
INSERT INTO #Table
( ID )
VALUES ( 'A00012' )
INSERT INTO #Table
( ID )
VALUES ( 'A00013' )
-- Insert Alumni
INSERT INTO #Table
( ID
)
SELECT 'A' + RIGHT('00000'
+ CAST(MAX(RIGHT(ID, LEN(ID) - 1)) + 1 AS VARCHAR(10)),
5)
FROM #Table
WHERE LEFT(ID, 1) = 'A'
-- Insert Non Alumni
INSERT INTO #Table
( ID
)
SELECT 'N' + RIGHT('00000'
+ CAST(MAX(RIGHT(ID, LEN(ID) - 1)) + 1 AS VARCHAR(10)),
5)
FROM #Table
WHERE LEFT(ID, 1) = 'N'

Iterating through a SQL Server 2008 R2 table variable and concatenate values

I have a table variable #Holding two columns: an id (not unique) and a message:
id message
---- -------
2 give
2 me
2 help
3 Need
3 help
1 help!
The result should be
2 give me help
3 Need help
1 help!
This it very much simplified, but shows that there are id which may exist more than once, and some kind of text which should be concatenated into a string.
I cannot manage it to loop through this table variable (but not through a table too!).
I tried a cursor (which I did not understand correctly) but it failed of course.
The number of records are not that much, not even 100 in that table variable.
Thanks yr. help
Michael
Original question
No CURSOR, WHILE loop, or User-Defined Function needed.
Just need to be creative with FOR XML and PATH.
[Note: This solution only works on SQL 2005 and later. Original question didn't specify the version in use.]
CREATE TABLE #YourTable ([ID] INT, [Name] CHAR(1), [Value] INT)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'A',4)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'B',8)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (2,'C',9)
SELECT
[ID],
STUFF((
SELECT ', ' + [Name] + ':' + CAST([Value] AS VARCHAR(MAX))
FROM #YourTable
WHERE (ID = Results.ID)
FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)')
,1,2,'') AS NameValues
FROM #YourTable Results
GROUP BY ID
DROP TABLE #YourTable
This should work if you are using SQL Server.
select T1.id,
stuff((select ' '+T2.[message]
from #A as T2
where T1.id = T2.id
for xml path(''), type).value('.', 'varchar(max)'), 1, 1, '') as [message]
from #A as T1
group by T1.id
Pretty the same, just less letters in the code:
DECLARE #t AS TABLE ( id INT, msg VARCHAR(100) );
INSERT INTO #t
VALUES ( 2, 'give' );
INSERT INTO #t
VALUES ( 2, 'me' );
INSERT INTO #t
VALUES ( 2, 'help' );
INSERT INTO #t
VALUES ( 3, 'Need' );
INSERT INTO #t
VALUES ( 3, 'help' );
INSERT INTO #t
VALUES ( 1, 'help!' );
SELECT DISTINCT
id ,
(
SELECT ST1.msg + ' '
FROM #t ST1
WHERE ST1.id = ST2.id
FOR XML PATH('')
) t
FROM
#t ST2;

SQL Server Simple Group by query

I have a simple problem , Although i believe its simple , am not able to figure out the same.
Consider i have the below table with exactly same data as given below :
CREATE TABLE #temp
(
link varchar(255),
number INT,
fname varchar(255)
)
insert into #temp VALUES ('abc',1,'f1')
insert into #temp VALUES ('abc',2,'f2')
insert into #temp VALUES ('abc',3,'f3')
insert into #temp VALUES ('abc',4,'f6')
insert into #temp VALUES ('abc',10,'f100')
insert into #temp VALUES ('abe',-1,'f0')
insert into #temp VALUES ('abe',1,'f1')
insert into #temp VALUES ('abe',2,'f2')
insert into #temp VALUES ('abe',3,'f3')
insert into #temp VALUES ('abe',4,'f6')
insert into #temp VALUES ('abe',20,'f200')
insert into #temp VALUES ('cbe',-1,'f0')
insert into #temp VALUES ('cbe',1,'f1')
insert into #temp VALUES ('cbe',2,'f2')
insert into #temp VALUES ('cbe',3,'f3')
Now for a given link , i need to get the max 'number' and the corresponding 'fname' which has the max 'number' for the given 'link'.
1)Ex : if link is 'abc' , output should be
abc, 10, f100
2)Ex : if link if 'abe' , Output should be
abe, 20, f200
3)Now link can be also given as a pattern , like (link like 'ab%') , so output should be
abc, 10, f100
abe, 20, f200
4)if (link like 'cb%') , so output should be
cbe, 3, f3
Any help in writing this group by query. I have a solution using CAST and string concat like below , but that seems to be in-efficient.
select link,number,fname from #temp
where link like 'ab%' and link+'_'+CAST(number AS varchar(255))
in (select link+'_'+CAST(MAX(number) AS varchar(255)) from #temp
group by link)
Thanks..
Using a self join:
SELECT x.link,
x.number,
x.fname
FROM #temp x
JOIN (SELECT t.link,
MAX(t.number) AS max_number
FROM #temp t
GROUP BY t.link) y ON y.link = x.link
AND y.max_number = x.number
Using a CTE and ROW_NUMBER (SQL Server 2005+):
WITH cte AS (
SELECT x.link,
x.number,
x.fname,
ROW_NUMBER() OVER(PARTITION BY x.link
ORDER BY x.number DESC) rank
FROM #temp x)
SELECT c.link,
c.number,
c.fname
FROM cte c
WHERE c.rank = 1