SQL Server: Storing Query Result into Variable - sql

I am trying to Concatenate Query Result for each Person into a Variable.
Cursor Extract Columname from #temp table and extract data from PersonTable
For Example:For PID=1(FName: John LName:Hill HomeCountry=US HomeState=CH)
123JohnHillUSCH
Person Table:
pid FName LName HomeCity HomeState
1 Pascal E New York NY
2 Steve F New York NY
CREATE table #Temp
([Id] int, [ColumnName] varchar(13))
;
INSERT INTO #Temp
([Id],[ColumnName])
VALUES
(1, 'EID'),
(2, 'FName'),
(3, 'LName'),
(4, 'HomeCountry'),
(5, 'HomeState')
;
SELECT * FROM #Temp
SET QUOTED_IDENTIFIER ON
SET ANSI_NULLS ON
GO
ALTER PROCEDURE [dbo].[up_Conv_GenerateResultsFromMappingTable]
#Param1 VARCHAR(30)
AS
SET NOCOUNT ON
DECLARE #ColName VARCHAR(100);
DECLARE #Result VARCHAR(MAX)
DECLARE #Cur as CURSOR;
SET #Cur = CURSOR FOR
SELECT columnName FROM #Temp
OPEN #Cur;
FETCH NEXT FROM #Cur INTO #ColName;
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #query nvarchar(MAX)
PRINT #Param1;
SET #Query='SELECT '+#ColName+' FROM dbo.PERSON where PID='+#Param1+''
PRINT #Query
EXECUTE sp_executesql #Query
, N'#Param1 varchar(30)',
#Param1
FETCH NEXT FROM #Cur INTO #ColName;
END
CLOSE #Cur;
DEALLOCATE #Cur;
Expected result
1,1PascalENewYorkNY
2,2SteveFNewYorkNY

In SQL Server 2012+, Why not just do
SELECT EID, CONCAT(EID, FName, LName, HomeCountry, HomeState) AS ConcatString, CreateDate
FROM Person
In SQL Server 2008R2 and below, it would have to be
SELECT EID, CAST(EID AS VARCHAR(20) + FName + LName + HomeCountry + HomeState AS ConcatString, CreateDate
FROM Person
And if You're wanting to return the results per user, you could parameterize it, and send in the EID individually from the source, or you could use a cursor...if you have absolutely no other option. If you're wanting to return the entire dataset, You can just consume the data straight from that dataset.

Related

Using cursor to loop through a table variable in SQL Server

I have a parameter of a stored procedure which gets some data in the format
1/1/2018-2/1/2018,2/1/2018-3/1/2018,3/1/2018-4/1/2018,4/1/2018-5/1/2018,5/1/2018-6/1/2018,6/1/2018-7/1/2018,7/1/2018-8/1/2018,8/1/2018-9/1/2018,9/1/2018-10/1/2018,
10/1/2018-11/1/2018,11/1/2018-12/1/2018,12/1/2018-12/31/2018
I have a function which splits the data based on the , character and stores the results into a table variable as shown here:
declare #SPlitDates table(ItemNumber int, Item nvarchar(max))
insert into #SPlitDates
select *
from dbo.SPlitFunction(#RequestData, ',')
After this I have to perform certain operations on the data range so I use cursors to loop through the temp table as shown below
DECLARE cur CURSOR FOR
SELECT Item
FROM #SPlitDates
ORDER BY ItemNumber
OPEN cur
FETCH NEXT FROM cur INTO #monthStart
WHILE ##FETCH_STATUS = 0
BEGIN
-- Some operation
END
The max data points that I will get in the temp table is the date range for 12 months.
My question is that could I be using something else apart from cursors to improve performance or it doesn't matter when the dataset is really this small.
Thanks
Edit - To show operation inside the cursor
declare #SPlitDates table(ItemNumber int, Item nvarchar(max))
insert into #SPlitDates
select *
from dbo.SPlitFunction(#RequestData, ',')
declare #SPlitDatesData table (ItemNumber varchar(100), Item nvarchar(max))
declare #SPlitDatesAvgData table(Code nvarchar(100), Val decimal(18,2))
declare #dataFilter as nvarchar(max),
#SQL as nvarchar(max);
declare #monthStart nvarchar(100)
declare #count int
set #count = 0
--Declaring a cursor to loop through all the dates as defined in the requested quarter
DECLARE cur CURSOR FOR
SELECT Item
FROM #SPlitDates
ORDER BY ItemNumber
OPEN cur
FETCH NEXT FROM cur INTO #monthStart
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #Period NVARCHAR(100)
SET #Period = #monthStart
INSERT INTO #SPlitDatesData
--split the dates to get the start and the end dates
SELECT *
FROM dbo.SPlitFunction(#Period, '-')
DECLARE #PeriodStart NVARCHAR(100)
DECLARE #PeriodEnd NVARCHAR(100)
SET #PeriodStart = (SELECT Item FROM #SPlitDatesData WHERE ItemNumber = 1)
SET #PeriodEnd = (SELECT Item FROM #SPlitDatesData WHERE ItemNumber = 2)
DELETE FROM #SPlitDatesData
--add the start and end dates to the filter
SET #dataFilter = 'StatusDate between convert(datetime,('''+#PeriodStart+'''))
and DATEADD(dy, 1, convert(datetime,('''+#PeriodEnd+''')))'
SET #count = #count +1;
SET #SQL = 'INSERT INTO #BidAverageCycleCalculation (SortOrder, Code, Data)
VALUES (#count,
''SL Payroll'',(select dbo.GetAverageCycleBetweenBids('''+#PeriodStart+''',
'''+#PeriodEnd+''',''SL''))
)'
EXEC SP_ExecuteSQL #SQL, N'#count int', #count;
SET #count = #count +1;
SET #SQL = 'INSERT INTO #BidAverageCycleCalculation (SortOrder, Code, Data)
VALUES (#count,
''GV Payroll'',(select dbo.GetAverageCycleBetweenBids('''+#PeriodStart+''',
'''+#PeriodEnd+''',''GV''))
)'
EXEC SP_ExecuteSQL #SQL , N'#count int', #count;
SET #count = #count +1;
SET #SQL = 'Insert into #BidAverageCycleCalculation (SortOrder,Code,Data)
Values (#count,
''Global Payroll'',(select dbo.GetAverageCycleBetweenBids('''+#PeriodStart+''',
'''+#PeriodEnd+''',''GVS''))
)'
EXEC SP_ExecuteSQL #SQL, N'#count int', #count;
SET #count = #count +1;
SET #SQL = 'Insert into #BidAverageCycleCalculation (SortOrder,Code,Data)
Values (#count,
''TimeHCM'',(select dbo.GetAverageCycleBetweenBids('''+#PeriodStart+''',
'''+#PeriodEnd+''',''Time''))
)'
EXEC SP_ExecuteSQL #SQL, N'#count int', #count;
delete from #SPlitDatesAVgData
FETCH NEXT FROM cur INTO #monthStart
END
CLOSE cur
DEALLOCATE cur
This uses two parts - first convert your string into a table, then do bulk inserts into your destination. No need for cursors.
** Please excuse any syntax errors as I'm doing it without access to your actual tables or functions so cant test it, but you get the idea
declare #in varchar(max)
set #in= '1/1/2018-2/1/2018,2/1/2018-3/1/2018,3/1/2018-4/1/2018,4/1/2018-5/1/2018,5/1/2018-6/1/2018,6/1/2018-7/1/2018,7/1/2018-8/1/2018,8/1/2018-9/1/2018,9/1/2018-10/1/2018,10/1/2018-11/1/2018,11/1/2018-12/1/2018,12/1/2018-12/31/2018'
declare #xml xml;
set #xml= convert(xml,'<r><f>'+replace(replace(#in,',','</t></r><r><f>'),'-','</f><t>') +'</t></r>')
declare #t table(id int identity, f date, t date)
insert #t
select
Tbl.Col.value('f[1]', 'date') f,
Tbl.Col.value('t[1]', 'date') t
FROM #xml.nodes('//r') Tbl(Col)
select * from #t
declare #count int;
select #count=count(*) from #t
INSERT INTO #BidAverageCycleCalculation (SortOrder, Code, Data)
select id, 'SL Payroll',(select dbo.GetAverageCycleBetweenBids(f,t,'SL')) from #t
INSERT INTO #BidAverageCycleCalculation (SortOrder, Code, Data)
select id+#count,'GV Payroll',(select dbo.GetAverageCycleBetweenBids(f,t,'GV')) from #t
INSERT INTO #BidAverageCycleCalculation (SortOrder, Code, Data)
select id+#count*2,'Global Payroll',(select dbo.GetAverageCycleBetweenBids(f,t,'GVS')) from #t
INSERT INTO #BidAverageCycleCalculation (SortOrder, Code, Data)
select id+#count*3,'TimeHCM',(select dbo.GetAverageCycleBetweenBids(f,t,'Time')) from #t

Create a dynamic query

I have made a stored procedure like this..
CREATE PROCEDURE [dbo].[sp_dataPull] #serverName nvarchar(30), #dbName nvarchar (30), #serverName2 nvarchar(30), #dbName2 nvarchar (30)
INSERT INTO sampleDatabase.dbo.WorkFlowCopy
([ServerName]
,[DBName]
,[ID]
,[ActivityDefinitionID]
,[ParentID]
,[Caption]
,[Description]
,[ShortDescription]
(SELECT #serverName, #dbName, sdb1.* from OPENDATASOURCE('SQLOLEDB',
'Data Source=phmnldb16\eaudit;user id=XXXX;password=XXXX').AUDIT_FSA_170_001.AUD170.Workflow sdb1)
UNION ALL
(SELECT #serverName2, #dbName2, sdb2.* from OPENDATASOURCE('SQLOLEDB',
'Data Source=phmnldb16\eaudit;user id=XXXX;password=XXXX').AUDIT_FSA_170_002.AUD170.Workflow sdb2)
This query's function is to move the the data from another server into specific table. Now, notice that I have 2 SELECT statements below and I have 4 parameters. It's because I combined 2 tables and moved it into another table.
The problem with this query is the fact that it is static. What If I need to combine 3 or more tables? Then I have to create another (SELECT and UNION ALL statement below and add another 2 or more parameters).. I want to make it DYNAMIC.
Try as follows:
CREATE PROCEDURE SP_DATAPULL #SERVER NVARCHAR(30),#DB NVARCHAR(30)
AS
BEGIN
DECLARE #SERVER NVARCHAR(MAX)='SERVER1,SERVER2,SERVER3,SERVER4,SERVER5'
DECLARE #DB NVARCHAR(MAX)='DB1,DB2,DB3,DB4,DB5'
DECLARE #SRV NVARCHAR(MAX),#DBN NVARCHAR(MAX),#QUERY NVARCHAR(MAX)
DECLARE #TEMP TABLE(SERVERNAME VARCHAR(100),DB VARCHAR(100))
WHILE(SELECT CHARINDEX(',',#SERVER))>0
BEGIN
SET #SRV=(SELECT SUBSTRING(#SERVER,0,CHARINDEX(',',#SERVER)))
SET #SERVER=SUBSTRING(#SERVER,LEN(#SRV)+2,LEN(#SERVER))
SET #DBN=(SELECT SUBSTRING(#DB,0,CHARINDEX(',',#DB)))
SET #DB=SUBSTRING(#DB,LEN(#DBN)+2,LEN(#DB))
INSERT INTO #TEMP VALUES (#SRV,#DBN )
END
INSERT INTO #TEMP VALUES (#SERVER,#DB )
SET #QUERY=' INSERT INTO sampleDatabase.dbo.WorkFlowCopy
([ServerName]
,[DBName]
,[ID]
,[ActivityDefinitionID]
,[ParentID]
,[Caption]
,[Description]
,[ShortDescription] '
DECLARE C CURSOR FOR
SELECT SERVERNAME,DB FROM #TEMP
OPEN C
FETCH NEXT FROM C INTO #SERVER,#DB
WHILE ##FETCH_STATUS=0
BEGIN
SET #QUERY=#QUERY+'(SELECT '''+#SERVER+''', '''+#DB+''', sdb1.* from OPENDATASOURCE(''SQLOLEDB'',
''Data Source=phmnldb16\eaudit;user id=XXXX;password=XXXX'').AUDIT_FSA_170_001.AUD170.Workflow sdb1) '
FETCH NEXT FROM C INTO #SERVER,#DB
END
CLOSE C
DEALLOCATE C
SET #QUERY=SUBSTRING(#QUERY,0,LEN(#QUERY)-LEN('UNION ALL'))
PRINT #QUERY
EXEC(#QUERY)
END

Declare a Cursor From a Table as Variable (In the Select-Statement)

I want to create a SP that have 2 String parameters for 2 table names. In the SP I use dynamic SQL to modify one of the tables, but the other is inside a cursor, and I cant use dynamic SQL after the "FOR"
ALTER PROCEDURE NameProcedure #SourceTable VARCHAR(100),#DestinationTable VARCHAR(100)
AS
BEGIN
DECLARE #AddressSource VARCHAR(100), #AddressDestination VARCHAR(100)
SELECT #AddressSource = '[Test_Toa].[dbo].[' + #SourceTable + ']'
SELECT #AddressDestination = '[Test_Toa].[dbo].[' + #DestinationTable + ']'
--Source Table columns
DECLARE #id int, #idmercado int, #idcadena int, #barcode nvarchar(255),#Complete_P nvarchar(MAX)
DECLARE #Cursor CURSOR
SET #Cursor = CURSOR FOR
--HEREE ITS MY PROBLEM :(!!!!!
SELECT id, idmercado, idcadena, barcode, precios + ',' FROM #AddressSource
OPEN #Cursor
FETCH NEXT
FROM #Cursor INTO #id,#idmercado,#idcadena,#barcode,#Complete_P
WHILE ##FETCH_STATUS = 0
BEGIN
--bla bla code
FETCH NEXT
FROM #Cursor INTO #id,#idmercado,#idcadena,#barcode,#Complete_P
END
CLOSE #Cursor
DEALLOCATE #Cursor
END
I just want to declare a cursor for the table that the user gives
Well, you need to write a dynamic sql statement. Just as an hint. You can copy the values from your given source table into a temp table, generate a cursor on the temp table, iterate through it and deallocate the cursor afterwards and drop the temp table. :-)
Here a short demo code:
DECLARE #sql nvarchar(max), #sourceTable nvarchar(255)
CREATE TABLE dbo.t1(id int, name nvarchar(200))
CREATE TABLE dbo.t2(id int, name nvarchar(200))
SET #sourceTable = N'dbo.t1'
CREATE TABLE #temp(id int, name nvarchar(200))
SET #sql = N'
INSERT INTO #temp(id,name)
SELECT id, name
FROM '+#sourceTable
EXEC(#sql)
DECLARE cur CURSOR FOR
SELECT id, name
FROM #temp
OPEN cur
DECLARE #id int, #name nvarchar(200)
FETCH NEXT FROM cur INTO #id, #name
WHILE ##fetch_status = 0 BEGIN
SELECT #id, #name -- demo output
FETCH NEXT FROM cur INTO #id, #name
END
-- cleanup
CLOSE cur
DEALLOCATE cur
DROP TABLE dbo.t1
DROP TABLE dbo.t2
DROP TABLE #temp
Beware, I just have written this in notepad without any database. But I'm quite sure it does it's job.
This just works if all available variants of #sourceTable have the same column specification. If not, you need to extract the needed columns from information schema and build a more dynamic code.

Pivoting SQL Server 2005 for unknown number of rows

My table is a dynamic one. E.g.:
id SUBJECT
1 his
2 math
3 sci
4 opt
5 ENG
6 SOC
The number of rows is not limited. There could be a hundred. I want output like this:
ID 1 2 3 4 5 6
HIS MATH SCI OPT ENG SOC
I could use a pivot query, but I would have to know the number of columns. How can I do this without knowing the number of columns in advance?
i got an answer but it's very tricky
create a table for all your records
count the records
create a table with that much number of columns
create a comma separated variable for the table which has records
then split the comma separated variables into multiple columns
here is the code
DECLARE #HEADDESC NVARCHAR(150)
DROP TABLE #HEADS
CREATE TABLE #HEADS
(
ID INT IDENTITY
,HEADS NVARCHAR(150)
,NU INT
)
DECLARE #NO INT;
SET #NO = 0
DECLARE C1 CURSOR FOR (
SELECT HEADDESC
FROM GMC.FEEHEAD_MASTER
WHERE CODE = 'GF' AND HEADDESC <> '')
OPEN C1
FETCH NEXT FROM C1 INTO #HEADDESC
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #HEADDESC
SET #NO = #NO+1
INSERT INTO #HEADS(HEADS,NU)
VALUES(#HEADDESC,#NO)
FETCH NEXT FROM C1 INTO #HEADDESC
END
--SELECT * FROM #HEADS
CLOSE C1
DEALLOCATE C1
DECLARE #COLNO INT
SET #COLNO = (SELECT COUNT(*) FROM #HEADS)
DECLARE #COLUMNS VARCHAR(8000)
SELECT #COLUMNS = COALESCE(#COLUMNS +','+ CAST(HEADS AS VARCHAR) ,
CAST(HEADS AS VARCHAR))
FROM #HEADS
--GROUP BY HEADS
DECLARE #value NVARCHAR(100)
SET #value = ',1,STUDENTIDNO,STUDENTNAME,'
SET #COLUMNS = #VALUE+#COLUMNS
SET #COLNO = #COLNO+4
--SELECT #COLUMNS
DROP TABLE #HEADSCOMMA
CREATE TABLE #HEADSCOMMA(HEADS NVARCHAR(3000))
INSERT INTO #HEADSCOMMA VALUES (#COLUMNS)
DROP TABLE #TEMP
CREATE TABLE #TEMP(COL1 NVARCHAR(1000))
DECLARE #SQL NVARCHAR(MAX)
DECLARE #COL NVARCHAR(1000)
DECLARE #COL1 INT
DECLARE #COLNAME NVARCHAR(1000)
SET #COL1 = 2
SET #COL = 'COL'
PRINT #COL1
--SET #COLNAME = #COL +CAST(#COL1 AS NVARCHAR(10))
WHILE #COL1 < =#COLNO
BEGIN
SET #COLNAME = #COL +CAST(#COL1 AS NVARCHAR(100))
PRINT #COLNAME
SET #SQL = 'ALTER TABLE #TEMP ADD '+#COLNAME+' NVARCHAR(100)'
EXEC(#SQL)
SET #COL1= #COL1+1
END
--SELECT * FROM #HEADSCOMMA -- COMMA SEPERATED VALUES
DECLARE #S VARCHAR(8000), #DATA VARCHAR(8000)
--DROP TABLE #NORMALISEDTABLE
--CREATE TABLE #NORMALISEDTABLE (HEADS NVARCHAR(200))
SELECT #S=''
WHILE EXISTS (SELECT * FROM #HEADSCOMMA WHERE HEADS>#S)
BEGIN
SELECT #S=HEADS FROM #HEADSCOMMA WHERE HEADS>#S
PRINT #S
SELECT #DATA=''''+REPLACE(#S,',',''',''')+''''
PRINT #DATA
INSERT INTO #TEMP
EXEC('SELECT '+#DATA)
END
SELECT * FROM #temp
will give the records
Dynamic SQL is an option.

How can I iterate over a recordset within a stored procedure?

I need to iterate over a recordset from a stored procedure and execute another stored procedure using each fields as arguments. I can't complete this iteration in the code. I have found samples on the internets, but they all seem to deal with a counter. I'm not sure if my problem involved a counter. I need the T-SQL equivalent of a foreach
Currently, my first stored procedure stores its recordset in a temp table, #mytemp. I assume I will call the secondary stored procedure like this:
while (something)
execute nameofstoredprocedure arg1, arg2, arg3
end
You need to create a cursor to loop through the record set.
Example Table:
CREATE TABLE Customers
(
CustomerId INT NOT NULL PRIMARY KEY IDENTITY(1,1)
,FirstName Varchar(50)
,LastName VARCHAR(40)
)
INSERT INTO Customers VALUES('jane', 'doe')
INSERT INTO Customers VALUES('bob', 'smith')
Cursor:
DECLARE #CustomerId INT, #FirstName VARCHAR(30), #LastName VARCHAR(50)
DECLARE #MessageOutput VARCHAR(100)
DECLARE Customer_Cursor CURSOR FOR
SELECT CustomerId, FirstName, LastName FROM Customers
OPEN Customer_Cursor
FETCH NEXT FROM Customer_Cursor INTO
#CustomerId, #FirstName, #LastName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #MessageOutput = #FirstName + ' ' + #LastName
RAISERROR(#MessageOutput,0,1) WITH NOWAIT
FETCH NEXT FROM Customer_Cursor INTO
#CustomerId, #FirstName, #LastName
END
CLOSE Customer_Cursor
DEALLOCATE Customer_Cursor
Here is a link to MSDN on how to create them.
http://msdn.microsoft.com/en-us/library/ms180169.aspx
This is why I used Raise Error instead of PRINT for output.
http://structuredsight.com/2014/11/24/wait-wait-dont-tell-me-on-second-thought/
It's very easy to loop through the rows in SQL procedure. You just need to use a cursor. Here is an example:
Let us consider a table Employee with column NAME and AGE with 50 records into it and you have to execute a stored procedure say TESTPROC which will take name and age parameters of each row.
create procedure CursorProc
as
begin
declare #count bigint;
declare #age varchar(500)
declare #name varchar(500)
select #count = (select count(*) from employee)
declare FirstCursor cursor for select name, age from employee
open FirstCursor
while #count > 0
begin
fetch FirstCursor into #name, #age
Exec TestProc #name, #age
set #count = #count - 1
end
close FirstCursor
deallocate FirstCursor
end
Make sure you deallocate the cursor to avoid errors.
try this (cursor free looping):
CREATE TABLE #Results (RowID int identity(1,1), Col1 varchar(5), Col2 int, ... )
DECLARE #Current int
,#End int
DECLARE #Col1 varchar(5)
,#Col2 int
,...
--you need to capture the result set from the primary stored procedure
INSERT INTO #Results
(Col1, COl2,...)
EXEC nameofstoredprocedure_1 arg1, arg2, arg3
SELECT #End=##ROWCOUNT,#Current=0
--process each row in the result set
WHILE #Current<#End
BEGIN
SET #Current=#Current+1
SELECT
#Col1=COl1, #Col2=Col2
FROM #Results
WHERE RowID=#Current
--call the secondary procedure for each row
EXEC nameofstoredprocedure_2 #Col1, #Col2,...
END
working example:
CREATE PROCEDURE nameofstoredprocedure_1
(#arg1 int, #arg2 int, #arg3 int)
AS
SELECT 'AAA',#arg1 UNION SELECT 'BBB',#arg2 UNION SELECT 'CCC',#arg3
GO
CREATE PROCEDURE nameofstoredprocedure_2
(#P1 varchar(5), #P2 int)
AS
PRINT '>>'+ISNULL(#P1,'')+','+ISNULL(CONVERT(varchar(10),#P2),'')
GO
CREATE TABLE #Results (RowID int identity(1,1), Col1 varchar(5), Col2 int)
DECLARE #Current int
,#End int
DECLARE #Col1 varchar(5)
,#Col2 int
INSERT INTO #Results
(Col1, COl2)
EXEC nameofstoredprocedure_1 111, 222, 333
SELECT #End=##ROWCOUNT,#Current=0
WHILE #Current<#End
BEGIN
SET #Current=#Current+1
SELECT
#Col1=COl1, #Col2=Col2
FROM #Results
WHERE RowID=#Current
EXEC nameofstoredprocedure_2 #Col1, #Col2
END
OUTPUT:
(3 row(s) affected)
>>AAA,111
>>BBB,222
>>CCC,333