Can we use EXEC() within a select statement - sql-server

/* Loop through tempTotal to update the result table */
SET #RowCount = 1
SELECT #MaxRows = COUNT(*) FROM #TempTotal
WHILE #RowCount <= #MaxRows
BEGIN
SELECT #RowCount = #RowCount + 1, #DeviceId = DeviceId, #SourceName = SourceName, #hrs = Hrs
FROM #TempTotal
WHERE rownum = #RowCount
EXEC ('UPDATE #TempInputSourceRpt SET [' + #SourceName + '] = ' + #Hrs + 'WHERE DeviceId = ' + #DeviceId)
END
I want to convert the above logic to remove looping and improve performance
Something like this would be really efficient
SELECT EXEC('UPDATE #TempInputSourceRpt SET [' + SourceName + '] = ' + Hrs + 'WHERE DeviceId = ' + DeviceId) FROM #TempTotal
but SQL does not allow to use EXEC within SELECT.
Any ideas/suggestions?

DECLARE #SQL Varchar(max)
SET #SQL = 'Use MyDatabase'
SELECT #SQL = SQL +
'UPDATE #TempInputSourceRpt SET [' +
SourceName + '] = ' +
Hrs +
'WHERE DeviceId = ' + DeviceId + CHAR(10) + CHAR(13) + ';'
FROM #TempTotal
PRINT #SQL
--EXEC (#SQL)
Run it with the EXEC remarked out first to see if it's what you need, then you can unremark it to run it.

You can select FROM an exec...

What version of sql-server? If 2008, MERGE is a great new function that would make this operation a lot easier.

Related

Getting timeout in a Mule application while processing the output of a big DB query

We've around 7 million records in Azure SQL DB. We have created a stored procedure to fetch paginated records. Following is the script:
CREATE PROCEDURE [dbo].[spGetCommodityReport]
(
#pgNum AS INT = 1,
#recPerPg AS INT = 100,
#factory AS NVARCHAR(max) = null,
#filter AS NVARCHAR(max) = null,
#isoWYF as VARCHAR(50) = null,
#isoWYT as VARCHAR(50) = null,
#columns as NVARCHAR(max) = null,
#measureCol AS NVARCHAR(max) = null
)
AS
BEGIN
SET NOCOUNT ON
DECLARE
#where AS NVARCHAR(2000),
#SQL AS NVARCHAR(max),
--sanitize local variables
SET #factory = ISNULL(#factory,'')
SET #filter = ISNULL(#filter,'')
SET #isoWYF = ISNULL(#isoWYF,'')
SET #isoWYT = ISNULL(#isoWYT,'')
SET #measureCol = ISNULL(#measureCol, '[food_qty]')
SET #where = '[cal_year_week] BETWEEN ' +char(39)+ #isoWYF +char(39) + ' AND ' + char(39) + #isoWYT + char(39)
IF(#factory != '')
BEGIN
SET #where = '[factory] IN ' + #factory + ' AND ' + #where
END
-- apply filters if requested
IF(#filter != '')
BEGIN
SET #where = #where + ' AND ' + #filter
END
--Prepare dynamic query
SET #SQL = 'SELECT ((totRec/' + CAST(#recPerPg AS VARCHAR(100)) + ') +1) [totPg],' + CAST( #PgNum AS VARCHAR(100)) +' [currPg],'+ CAST(#RecPerPg AS VARCHAR(100)) +' [recPerPg], * FROM ('
SET #SQL = #SQL + 'SELECT * '
SET #SQL = #SQL + ' from
(SELECT
count(*) over() [totRec]
,[rec_no]
,[factory]
,[city]
,[food]
,[drink]
,[ingre]
,[cal_year_week]
,' + #measureCol +
',[audit_region]
FROM [dbo].[TBLFOOD] WITH (NOLOCK) WHERE'
+ #where
SET #SQL = #SQL + ') as SRC PIVOT(SUM('+ #measureCol +') for [cal_year_week] in ('+ #columns +')) as PVT) AS FOODTBL '
SET #SQL = #SQL + ' ORDER BY factory'
if(#recPerPg > 0)
BEGIN
SET #SQL = #SQL + ' OFFSET ' + CAST( #recPerPg * (#pgNum - 1) AS VARCHAR(100)) + ' ROWS '
SET #SQL = #SQL + ' FETCH NEXT ' + CAST( #recPerPg AS VARCHAR(100)) + ' ROWS ONLY;'
END
print(#SQL)
EXEC(#SQL)
END
In SYS API we enrich the response and our EXP API is being consumed by front-end app. We don't have Process API in this project.
We're doing transformation which is discussed here: Transformation of Payload to custom object using DataWeave 2.0
This is working as expected in dev environment where we have around 50k records. When we promote this to higher environment where we have around 7 million records, we're getting timed-out exceptions. Tried a few things, increase the response time-out in both EXP API and SYS API but no luck.
While ran following statement in DB (having 7 million records), it returns 100 records as per criteria in less than a second:
EXEC [dbo].[spGetCommodityReport] #factory = '0415', #filter = " city != '0099' ", #isoWYF = "[202201]" , #isoWYT = "[202203]", #columns = "[202201], [202202], [202203]"
My question:
What is the best way to fix this time-out issue?

How to avoid while loop

I have one query and because of this query execution time is increasing and taking time to execute in SQL Server 2016.
SET #Count = (SELECT COUNT(1) FROM #SlabWeight);
WHILE ( #ID <= #Count )
BEGIN
SELECT #Weight = CONVERT(VARCHAR(11), weight)
FROM #SlabWeight
WHERE ID = #ID;
SET #WeightList += '[' + #Weight + '],';
SET #SQL = #SQL + ' ALTER TABLE #TempData ADD ['
+ #Weight + '] DECIMAL(18,2), [' + #Weight
+ '_BasedOn] VARCHAR(10)';
SET #SelectStatment = #SelectStatment + ' , [' + #Weight + '] , [' + #Weight + '_BasedOn]';
SET #ID += 1;
END;
Can anyone tell me how to avoid using a while loop in this query?

How to SELECT and UNION from a group of tables in schema in SQL Server 2008 R2 using a variable to define the database

This is a progression from the question asked here: How to SELECT and UNION from a group of Tables in the schema in SQL Server 2008 R2
I would like to do very much the same thing and the answer given by MarkD works perfectly for the database I am currently working with. Although admittedly I'd like to understand exactly how. How does the query below build the union query from the list of tables returned by the information_schema?
DECLARE #Select_Clause varchar(600) = N'SELECT [Patient_Number] AS [ID number]
,[Attendance Date] AS [Date Seen]
,[Attendance_Type] AS [New/Follow up]
,[Episode Type] AS [Patient Type]
,[Local Authority District]
,Postcode, N''Shaw'' AS Clinic '
,#Where_Clause varchar(100) = N' WHERE [EPISODE TYPE] LIKE N''HIV'''
,#Union_Clause varchar(100) = N' UNION ALL '
,#Query nvarchar(max) = N''
,#RawDataBase varchar(50) = N'BHT_1819_RawData'
,#Schema varchar(50) = N'HIVGUM'
,#Table_Count tinyint;
DECLARE #Table_Count_def nvarchar(100) = N'#TableSchema varchar(50)
,#Table_CountOUT tinyint OUTPUT'
,#Start_Position int = LEN(REPLACE(#Select_Clause, N' ', N'-'))
,#Length int;
SET #Query = N'SELECT #Table_CountOUT = COUNT(*) FROM ' + #RawDataBase +
N'.INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA LIKE #TableSchema';
EXEC sp_executesql #query, #Table_Count_def, #TableSchema=#Schema,
#Table_CountOUT=#Table_Count OUTPUT;
SET #Query = N'';
IF #Table_Count > 0
Begin
IF OBJECT_ID(N'dbo.HIV_Cumulative', N'U') is not null
DROP TABLE dbo.HIV_Cumulative;
SELECT #Query = #Query + #Select_Clause + N' FROM ' + #RawDataBase +
N'.HIVGUM.' + TABLE_NAME + #Where_Clause + #Union_Clause
FROM BHT_1819_RawData.INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA LIKE #Schema;
SET #Length = LEN(REPLACE(#query, N' ', N'-')) - #Start_Position -
LEN(REPLACE(#Where_Clause + #Union_Clause, N' ', N'-'));
SELECT #Query = SUBSTRING(#QUERY , #Start_Position+1, #Length)
SET #Query = #Select_Clause + N' INTO BHT_SLR..HIV_Cumulative ' + #QUERY
+ #Where_Clause;
EXEC sp_executesql #Query
End
ELSE
PRINT N'No tables present in database ' + #RawDataBase + N' for Schema ' +
#Schema + N'. You must import source data first.';
The added complication is that I am querying the tables on a separate DB - currently BHT_1819_RawData - so have hard coded the database where it queries the information_schema. What I would really like to do is to specify the separate database using a variable. So that it can be reconfigured to extract from BHT_1920_RawData. I am fairly familiar with exec and sp_executesql, but have only occasionally used output parameters so am not sure what is required here. The attempts that I have made haven't worked. Once I have got this right, I will need to create several other similar scripts that work on the same principle.
Once I realised what needed to happen, I went through some trial and error and came up with a solution:
SET #ParmDef = N'#QueryOut nvarchar(2500) OUTPUT';
SET #sql_string = N'SELECT #QueryOut = #QueryOut + N'''
+ #Select_Clause + ' FROM '
+ #RawDataBase
+ N'.[' + #Schema + N'].'' + TABLE_NAME + N'' '
+ #Where_Clause
+ #Union_Clause
+ N''' FROM '
+ #RawDataBase
+ N'.INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA LIKE '''
+ #Schema
+ N''' AND TABLE_NAME NOT LIKE N''%_YTD%''';
EXEC sp_executesql #sql_string, #ParmDef, #QueryOut=#Query OUTPUT;
SET #Length = LEN(REPLACE(#query, N' ', N'-')) - #Start_Position -
LEN(REPLACE(#Where_Clause + #Union_Clause, N' ', N'-'));
SELECT #Query = SUBSTRING(#QUERY , #Start_Position, #Length+1);
SET #Query = REPLACE(#Select_Clause, N'''''', '''') + N' INTO ' + #New_Table + N' ' +
#QUERY + REPLACE(#Where_Clause, N'''''', '''');
EXEC sp_executesql #Query;

Assign year variable to column alias for excel sheet

I have an excel sheet which is bound to a stored procedure. In the stored procedure I am selecting the columns that appear in the excel sheet. Now I'm facing an issue when I wanted to add some more columns:
Some value 2016
Some value 2017
Some value 2018
The first column is adding the actual year to the header and the two others the next one and the year after the next one.
My problem is that I don't know how to do this dynamically. I've tried something like this:
DECLARE #actualYear INT = YEAR(GETDATE())
SELECT tab.Name,
myTable.SomeValue [Some value #actualYear],
myTableNext.SomeValue [Some value #actualYear+1],
myTableAfterTheNext.SomeValue [Some value #actualYear+2]
FROM SomeTable tab
LEFT JOIN MyTable myTable ON tab.SomeId = myTable.SomeId
AND myTable.[Year] = #actualYear
LEFT JOIN MyTable myTableNext ON tab.SomeId = myTableNext.SomeId
AND myTable.[Year] = (#actualYear+1)
LEFT JOIN MyTable myTableAfterTheNext ON tab.SomeId = myTableAfterTheNext.SomeId
AND myTable.[Year] = (#actualYear+2)
The output is the following:
+------+------------------------+--------------------------+--------------------------+
| Name | Some value #actualYear | Some value #actualYear+1 | Some value #actualYear+2 |
+------+------------------------+--------------------------+--------------------------+
Second try:
SELECT tab.Name,
myTable.SomeValue ['Some value' + #actualYear]
...
Output:
+------+----------------------------+ ...
| Name | 'Some value' + #actualYear | ...
+------+----------------------------+ ...
How can I get the correct column headers dynamically?
You'll have to create a dynamic sql query like so (short example):
declare #i int;
declare #sql nvarchar(max);
set #i = 2016;
set #sql = N'select 1 as [' + cast(#i as nvarchar) + N']';
exec(#sql);
Translated to your sql query this should be something like this:
declare #sql nvarchar(max);
declare #actualYear int = year(getdate());
set #sql = #sql + N'select tab.Name, '
set #sql = #sql + N' myTable.SomeValue [' + cast(#actualYear as nvarchar) + N'], '
set #sql = #sql + N' myTableNext.SomeValue [' + cast(#actualYear + 1 as nvarchar) + N'], '
set #sql = #sql + N' myTableAfterTheNext.SomeValue [' + cast(#actualYear + 2 as nvarchar) + N'] '
set #sql = #sql + N'from SomeTable tab '
set #sql = #sql + N'left join MyTable myTable '
set #sql = #sql + N'on tab.SomeId = myTable.SomeId '
set #sql = #sql + N' and myTable.Year = #actualYear '
set #sql = #sql + N'left join MyTable myTableNext '
set #sql = #sql + N'on tab.SomeId = myTableNext.SomeId '
set #sql = #sql + N' and myTable.Year = (#actualYear + 1) '
set #sql = #sql + N'left join MyTable myTableAfterTheNext '
set #sql = #sql + N'on tab.SomeId = myTableAfterTheNext.SomeId '
set #sql = #sql + N' and myTable.Year = (#actualYear + 2); '
exec(#sql);
How to easily convert an SQL query into a dynamic SQL query:
Note, within Notepad++ you should replace the regular expression ^(.*)$ with set #sql = #sql + N'\1 '.
Update
Possible implementation of the above into a stored procedure (short example only):
IF OBJECT_ID('procTest', 'P') IS NOT NULL
DROP PROCEDURE procTest;
GO
CREATE PROCEDURE procTest
AS
BEGIN
DECLARE #i INT;
DECLARE #sql NVARCHAR(MAX);
SET #i = 2016;
SET #sql
= N'insert into #t (Column1) VALUES (' + CAST(#i AS NVARCHAR)
+ N'); ' + N'insert into #t (Column1) '
+ N'SELECT cast(1 as nvarchar) as [' + CAST(#i AS NVARCHAR) + N']';
EXEC (#sql);
END;
GO
CREATE TABLE #t
(
Column1 NVARCHAR(MAX)
);
EXEC dbo.procTest;
SELECT *
FROM #t;
DROP TABLE #t;

alternative solution for while loop in sql

How can I write following code without using while loop?? Is there any alternative way to avoid while loop in this case??
SELECT #count = COUNT(*) from CommonTables
While(#count > 0)
BEGIN
select top 1 #Sname = Schema_name,#Tname = Name from CommonTables
SET #sql = ''
SET #sql = 'insert into #Temp1 select '''+#Sname+''','''+#Tname+''',column_name,data_type,character_maximum_length FROM '+#DB1+'.information_schema.columns
WHERE table_name = '''+#Tname+''' and TABLE_SCHEMA = '''+#Sname+''''
EXEC SP_EXECUTESQL #SQL
SET #sql = ''
SET #sql = 'insert into #Temp2 select '''+#Sname+''','''+#Tname+''',column_name,data_type,character_maximum_length FROM '+#DB2+'.information_schema.columns
WHERE table_name = '''+#Tname+''' and TABLE_SCHEMA = '''+#Sname+''''
EXEC SP_EXECUTESQL #SQL
DELETE from CommonTables where Name = #Tname and Schema_name = #Sname
SELECT #count = COUNT(*) from CommonTables
END
Here CommonTables contain list of tables which are common in 2 databases (DB1 and DB2)
don't need loop at all.
select #sql = isnull(#sql, '')
+ N'INSERT INTO #Temp1 '
+ N'SELECT table_schema, table_name,column_name,data_type,character_maximum_length '
+ N'FROM ' + QUOTENAME(#DB1) + '.INFORMATION_SCHEMA.COLUMNS '
+ N'WHERE table_name = ''' + Name + ''' and TABLE_SCHEMA = ''' + Schema_name + ''';' + char(13)
from CommonTables
print #sql
exec sp_executesql #sql
if you want don't need 2 separate sp_execute statement for 2 DB. You can combine into one single sp_execute
and here is the query
select #sql = isnull(#sql, '')
+ N'insert into ' + db.temptbl + ' '
+ N'SELECT TABLE_SCHEMA,TABLE_NAME,COLUMN_NAME,DATA_TYPE,CHARACTER_MAXIMUM_LENGTH '
+ N'FROM ' + QUOTENAME(db.dbname) + '.INFORMATION_SCHEMA.COLUMNS '
+ N'WHERE TABLE_NAME = ''' + Name + ''' AND TABLE_SCHEMA = ''' + Schema_name + ''';' + char(13)
from CommonTables
cross join
(
select temptbl = '#Temp1', dbname = 'DB1' union all
select temptbl = '#Temp2', dbname = 'DB2'
) db
print #sql
exec sp_executesql #sql

Resources