Temporary tables in a WHILE LOOP - sql-server

I have a question concerning a dynamic creation of temporary tables.
I need a while loop in which temporary tables are created in every cycle differing in their name using the variable #i. So I specified the variables #CreateTableVerbGES and #ShowTableVerbGES with the code as strings and want to execute them in the loop.
Here is my code:
DECLARE #i int = 1
DECLARE #CreateTableVerbGES nvarchar(max)
DECLARE #ShowTableVerbGES nvarchar (max)
WHILE #i<4
BEGIN
SET #CreateTableVerbGES = 'CREATE TABLE #UnterBaugruppe'+CAST((#i) as nvarchar(max))+' ([IDTBG] [int] identity(1,1),[IDGES] [int] NULL,[Baugruppe1] [nvarchar](max) NULL,[IDBG1] [int] NULL,[Bauteil1] [int] NULL,[Baugruppe2] [nvarchar](max) NULL,[IDBG2] [int] NULL,[Bauteil2] [int] NULL,[Baugruppe3] [nvarchar](max) NULL,[IDBG3] [int] NULL,[Bauteil3] [int] NULL)'
SET #ShowTableVerbGES = 'SELECT * FROM #UnterBaugruppe'+CAST((#i) as nvarchar(max))
EXEC (#CreateTableVerbGES)
EXEC (#ShowTableVerbGES)
SET #i += 1
END
The problem is that it seems like the first cycle of the loop is running, because the first table #UnterBaugruppe1 is shown. But for the next two cycles there is a warning that the tables #UnterBaugruppe2 and #UnterBaugruppe3 are invalid and don't exist.
Does anybody have an idea why it isn't working?
I really need the tables in a loop because I want to insert different information in the loop as well dependent on #i.

I just concatenated the dynamic sql of two variable into single using new line character and executed and it perfectly worked for me!
DECLARE #i int = 1
DECLARE #CreateTableVerbGES nvarchar(max)
DECLARE #ShowTableVerbGES nvarchar (max)
WHILE #i<4
BEGIN
SET #CreateTableVerbGES = 'CREATE TABLE #UnterBaugruppe'+CAST((#i) as nvarchar(max))+' ([IDTBG] [int] identity(1,1),[IDGES] [int] NULL,[Baugruppe1] [nvarchar](max) NULL,[IDBG1] [int] NULL,[Bauteil1] [int] NULL,[Baugruppe2] [nvarchar](max) NULL,[IDBG2] [int] NULL,[Bauteil2] [int] NULL,[Baugruppe3] [nvarchar](max) NULL,[IDBG3] [int] NULL,[Bauteil3] [int] NULL)'
+ CHar(10) + Char(13) + 'SELECT * FROM #UnterBaugruppe'+CAST((#i) as nvarchar(max)) + CHar(10) + Char(13)
Print #CreateTableVerbGES
EXEC (#CreateTableVerbGES)
--EXEC (#ShowTableVerbGES)
SET #i += 1
END

Related

Executing a stored procedure by picking SQL variable from a list in T-SQL without using Cursor

I have a list of values, and i need the variable to pick it up one by one and execute the commands below. I need to achieve something like below in T-SQL. Is it possible without cursors?
SET NOCOUNT ON;
DECLARE #IPA VARCHAR(10)
FOR #IPA IN ['ADV, 'AC','AHA','ALPEB','AG','APCWEB]
IF OBJECT_ID('[ESProcess].[dbo].[EJ_Test]') IS NOT NULL
DROP TABLE [ESProcess].[dbo].[EJ_Test]
CREATE TABLE [ESProcess].[dbo].[EJ_Test]
(
[PRIM] [varchar](50) NULL,
[CLAIM_ID] [varchar](100) NULL,
[P_CLAIMNO] [varchar](100) NULL,
[Pro] [varchar](3) NULL,
[VALUE] [varchar](50) NULL,
[ErrorCode] [int] NULL,
[DESCRIP] [varchar](500) NULL,
[FILENAME] [varchar](300) NULL
)
INSERT INTO [ESProcess].[dbo].[EJ_Test]
SELECT
PRIM,a.CLAIM_ID,P_CLAIMNO, Pro, u.VALUE,
NULL AS ErrorCode, NULL AS DESCRIP, [FILENAME]
FROM
[SPID].[#IPA].[dbo].837_in_1 a
JOIN
[SPID].[#IPA].[dbo].[837_In_U] u ON a.ED_ID = u.M_ID
WHERE
a.CREATEDATE >= '20221001'
AND u.FNO = '20'
AND Pro = 'D'
END
Here. Note that drop-creating the table means that at the end you will only have data from APCWEB.
SET NOCOUNT ON;
DECLARE #IPA VARCHAR(10)
declare #sql nvarchar(max)
create table #ipas
(
ord int
,ipa varchar(10)
)
insert #ipas(ord,ipa) values
(1,'ADV')
,(2,'AC')
,(3,'AHA')
,(4,'ALPEB')
,(5,'AG')
,(6,'APCWEB')
declare ipa_cursor cursor local fast_forward for
select ipa from #ipas order by ord
open ipa_cursor
while 1=1
begin
fetch next from ipa_cursor into #IPA
if ##fetch_status<>0 break
set #sql=N''+
'IF OBJECT_ID(''[ESProcess].[dbo].[EJ_Test]'') IS NOT NULL
DROP TABLE [ESProcess].[dbo].[EJ_Test]
CREATE TABLE [ESProcess].[dbo].[EJ_Test]
(
[PRIM] [varchar](50) NULL,
[CLAIM_ID] [varchar](100) NULL,
[P_CLAIMNO] [varchar](100) NULL,
[Pro] [varchar](3) NULL,
[VALUE] [varchar](50) NULL,
[ErrorCode] [int] NULL,
[DESCRIP] [varchar](500) NULL,
[FILENAME] [varchar](300) NULL
)
INSERT INTO [ESProcess].[dbo].[EJ_Test]
SELECT
PRIM,a.CLAIM_ID,P_CLAIMNO, Pro, u.VALUE,
NULL AS ErrorCode, NULL AS DESCRIP, [FILENAME]
FROM
[SPID].'+quotename(#IPA)+'.[dbo].837_in_1 a
JOIN
[SPID].'+quotename(#IPA)+'.[dbo].[837_In_U] u ON a.ED_ID = u.M_ID
WHERE
a.CREATEDATE >= ''20221001''
AND u.FNO = ''20''
AND Pro = ''D'''
exec sp_executesql #sql
end -- cursor while
close ipa_cursor
deallocate ipa_cursor

SELECT sp_WhoIsActive into Table

I'm trying to script sp_WhoIsActive into a table. The goal is feed the table with an Agent Job every 10 seconds.
I followed this guide and I tried to feed the table this way:
--Log activity into table.
DECLARE #destination_table VARCHAR(4000) =
'[Monitoring].[dbo].[WhoIsActive] '
EXEC sp_WhoIsActive
#get_plans = 1,
#get_transaction_info = 1,
#destination_table = #destination_table;
But as result I receive the error:
Warning: The join order has been enforced because a local join hint is used.
Msg 50000, Level 16, State 1, Procedure sp_WhoIsActive, Line 1111 [Batch Start Line 0]
Destination table not properly formatted.
On Google I found many guides talking about a solution that could help me execute a Stored Procedure into a temp table and from there I could create a table:
sp_configure 'Show Advanced Options', 1
GO
RECONFIGURE
GO
sp_configure 'Ad Hoc Distributed Queries', 1
GO
RECONFIGURE
GO
SELECT * INTO #MyTempTable FROM OPENROWSET('SQLNCLI', 'Server=localhost;Trusted_Connection=yes;',
'EXEC sp_WhoIsActive')
SELECT * FROM #MyTempTable
But this process too is failing with error:
Msg 11526, Level 16, State 1, Procedure sys.sp_describe_first_result_set, Line 1 [Batch Start Line 12]
The metadata could not be determined because statement 'INSERT #sessions
(
recursion,
session_id,
request_id' in procedure 'sp_WhoIsActive' uses a temp table.
I tried following Kendra Little blog but that too is not working.
In the end I scripted out the table manually:
CREATE TABLE [dbo].[WhoIsActive](
[dd_hh_mm_ss_mss] [nvarchar](50) NOT NULL,
[session_id] [tinyint] NOT NULL,
[sql_text] [nvarchar](max) NOT NULL,
[sql_command] [nvarchar](400) NOT NULL,
[login_name] [nvarchar](50) NOT NULL,
[wait_info] [nvarchar](50) NOT NULL,
[tran_log_writes] [nvarchar](50) NOT NULL,
[CPU] [smallint] NOT NULL,
[tempdb_allocations] [smallint] NOT NULL,
[tempdb_current] [smallint] NOT NULL,
[blocking_session_id] [nvarchar](50) NOT NULL,
[reads] [int] NOT NULL,
[writes] [float] NOT NULL,
[physical_reads] [tinyint] NOT NULL,
[query_plan] [nvarchar](50) NOT NULL,
[used_memory] [tinyint] NOT NULL,
[status] [nvarchar](50) NOT NULL,
[tran_start_time] [datetime2](7) NOT NULL,
[implicit_tran] [nvarchar](50) NOT NULL,
[open_tran_count] [tinyint] NOT NULL,
[percent_complete] [nvarchar](50) NOT NULL,
[host_name] [nvarchar](50) NOT NULL,
[database_name] [nvarchar](50) NOT NULL,
[program_name] [nvarchar](100) NOT NULL,
[start_time] [datetime2](7) NOT NULL,
[login_tine] [datetime2](7) NOT NULL,
[request_id] [tinyint] NOT NULL,
[collection_time] [datetime2](7) NOT NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
But that too is failing and I cannot feed the table with a job.
sp_WhoIsActive is so popular, I cannot believe I'm the only one trying to insert the results into a table.
You need to create a table suitable to use with the output of the procedure, the schema of which can vary depending on the options you use.
SP_WhoIsActive will actually give you the create script, so to capture the default options just do
declare #definition varchar(max)
exec sp_WhoIsActive #return_schema = 1, #schema = #definition output
print #definition
This returns the appropriate T-SQL:
CREATE TABLE < table_name > (
[dd hh:mm:ss.mss] VARCHAR(8000) NULL
,[session_id] SMALLINT NOT NULL
,[sql_text] XML NULL
,[login_name] NVARCHAR(128) NOT NULL
,[wait_info] NVARCHAR(4000) NULL
,[CPU] VARCHAR(30) NULL
,[tempdb_allocations] VARCHAR(30) NULL
,[tempdb_current] VARCHAR(30) NULL
,[blocking_session_id] SMALLINT NULL
,[reads] VARCHAR(30) NULL
,[writes] VARCHAR(30) NULL
,[physical_reads] VARCHAR(30) NULL
,[used_memory] VARCHAR(30) NULL
,[status] VARCHAR(30) NOT NULL
,[open_tran_count] VARCHAR(30) NULL
,[percent_complete] VARCHAR(30) NULL
,[host_name] NVARCHAR(128) NULL
,[database_name] NVARCHAR(128) NULL
,[program_name] NVARCHAR(128) NULL
,[start_time] DATETIME NOT NULL
,[login_time] DATETIME NULL
,[request_id] INT NULL
,[collection_time] DATETIME NOT NULL
)
Edit and run with the required target table name, then you can run sp_whoisactive with the destination table option
exec sp_WhoIsActive #destination_table='Monitoring.dbo.WhoIsActive'
See the docs

Generation Sequence CertificateNo

I have block code for generation for CertificateNo. I need anyone check performance my block code
--Struct table config
--drop table CertificateSequenceGen;
CREATE TABLE [dbo].[CertificateSequenceGen](
[Bus] [varchar](15) NULL,
[MonthNo] [varchar](4) NOT NULL,
[DateStart] [date] NULL,
[DateEnd] [date] NULL,
[SequenceStart] [int] NOT NULL,
[SequenceEnd] [int] NOT NULL,
[CurrentNo] [int] NOT NULL
) ON [PRIMARY]
---initial default data into table
INSERT INTO [dbo].[CertificateSequenceGen]([Bus],[MonthNo],[DateStart],[DateEnd],[SequenceStart],[SequenceEnd],[CurrentNo])
VALUES('ABC','1902','2019-02-01','2019-03-01',1,9999999,1);
--Store for generate certificateN
--drop PROC prdGetCertificateNo
create PROC prdGetCertificateNo
#Bus varchar(15) = '',
#Prefix varchar(15) = '',
#CertificateNo varchar(31) OUTPUT
as
begin
update
CertificateSequenceGen
set
#CertificateNo = #Prefix + Bus + MonthNo + left (REPLICATE('0',7 - len(CurrentNo)) + cast (CurrentNo as varchar(7)),7),
CurrentNo = CurrentNo + 1
where
Bus = #Bus and GETDATE() between DateStart and DateEnd;
end
/*
--execute procedure genarate the CertificateNo
declare #Prefix varchar(15) = 'YZ', #ProductCode varchar(15) = 'ABC', #CertificateNo varchar(31);
exec [dbo].[prdGetCertificateNo] #ProductCode, #Prefix, #CertificateNo output
select #CertificateNo;
*/
after exec procedure below is result
--result---
YZABC19020000001

SQL Server Always Encrypted Operand type clash: varchar is incompatible with varchar(60) when running EXEC sproc

I am unable to EXEC a stored procedure that upserts a table that has an encrypted column using Always Encrypted. However, I am able to copy the SQL from the sproc and run that as regular SQL with the parameters set, Just cannot get the sproc to fire when executing the sproc via the EXEC function in SSMS which is also causing problems in the application
The table has a trigger on it that inserts into another audit table of similar structure that is also encrypted using the same encryption. I have done the usual thing:
Checking the Enable Parameterizaion for Always Encrypted in Query Options Setting column encryption setting=enabled on the Connection Settings.
Refreshing the encyrption metadata for the sproc:
EXEC sp_refresh_parameter_encryption 'organization.uspOrganizationAddressUpsert'
Tables:
CREATE TABLE [organization].[OrganizationAddress](
[OrganizationAddressId] [int] IDENTITY(1,1) NOT NULL,
[CreatedUser] [int] NOT NULL,
[CreatedDate] [datetime2](7) NOT NULL,
[LastUpdateUser] [int] NOT NULL,
[LastUpdateDate] [datetime2](7) NOT NULL,
[RemovedDate] [datetime2](7) NULL,
[Address1] [varchar](60) NOT NULL,
[Address2] [varchar](60) NULL,
[City] [varchar](60) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [CEK_Auto1], ENCRYPTION_TYPE = Deterministic, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NOT NULL,
[State] [varchar](60) NOT NULL,
[ZipCode] [varchar](60) NOT NULL,
[ClientNumberId] [int] NOT NULL
CREATE TABLE [audit].[OrganizationAddressAudit](
[OrganizationAddressId] [int] NOT NULL,
[CreatedUser] [int] NOT NULL,
[CreatedDate] [datetime2](7) NOT NULL,
[LastUpdateUser] [int] NOT NULL,
[LastUpdateDate] [datetime2](7) NOT NULL,
[RemovedDate] [datetime2](7) NULL,
[Address1] [varchar](60) NOT NULL,
[Address2] [varchar](60) NULL,
[City] [varchar](60) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [CEK_Auto1], ENCRYPTION_TYPE = Deterministic, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NOT NULL,
[State] [varchar](60) NOT NULL,
[ZipCode] [varchar](60) NOT NULL,
[ClientNumberId] [int] NOT NULL,
[OperationDate] [datetime] NOT NULL,
[Operation] [varchar](50) NOT NULL,
[OperationBy] [varchar](100) NOT NULL
Stored Procedure:
ALTER PROCEDURE [organization].[uspOrganizationAddressUpsert]
#OrganizationId INT,
#ExecutingUserId INT,
#Address1 VARCHAR(60),
#Address2 VARCHAR(60),
#City VARCHAR(60),
#State VARCHAR(60),
#ZipCode VARCHAR(60),
#ClientNumberId INT
AS
BEGIN
SET NOCOUNT ON
DECLARE #RightNow AS DATETIME2 = SYSDATETIME()
If EXISTS (Select 1 From [organization].[OrganizationAddress] Where ClientNumberId = #ClientNumberId)
BEGIN
UPDATE [organization].[OrganizationAddress] SET
LastUpdateUser = #ExecutingUserId,
LastUpdateDate = #RightNow,
Address1 = #Address1,
Address2 = #Address2,
City = #City,
[State] = #State,
ZipCode = #ZipCode,
RemovedDate = Null
Where ClientNumberId = #ClientNumberId
END
ELSE
BEGIN -- INSERT part of the UPSERT
INSERT INTO [organization].[OrganizationAddress]
(CreatedUser
,CreatedDate
,LastUpdateUser
,LastUpdateDate
,Address1
,Address2
,City
,[State]
,ZipCode
,ClientNumberId)
VALUES
(#ExecutingUserId
,#RightNow
,#ExecutingUserId
,#RightNow
,#Address1
,#Address2
,#City
,#State
,#ZipCode
,#ClientNumberId)
END
END
Running the stored procedure code with the paramteers set is fine, but I am unable to EXEC the sproc:
declare #orgId INT = 1;
declare #client int = 888;
declare #user int = 1;
declare #Add1 varchar(60)= 'Test Address1';
declare #Add2 varchar(60)= 'Test Address2';
declare #city varchar(60) = 'City';
declare #state varchar(60) = 'St';
declare #zip varchar(60) = '12345';
EXEC organization.uspOrganizationAddressUpsert
#OrganizationID=#orgID,
#ExecutingUserId = #user, -- int
#Address1 = #Add1, -- varchar(60)
#Address2 = #Add2, -- varchar(60)
#City = #city, -- varchar(60)
#State = #state, -- varchar(60)
#ZipCode = #zip, -- varchar(60)
#ClientNumberId = #client; -- int
Msg 206, Level 16, State 2, Procedure uspOrganizationAddressUpsert, Line 0 [Batch Start Line 1]
Operand type clash: varchar is incompatible with varchar(60) encrypted with (encryption_type = 'DETERMINISTIC', encryption_algorithm_name = 'AEAD_AES_256_CBC_HMAC_SHA_256', column_encryption_key_name = 'CEK_Auto1', column_encryption_key_database_name = 'BankruptcyApp') collation_name = 'SQL_Latin1_General_CP1_CI_AS'
I created a similar table in a test DB with VARCHAR(60) for the enrcrypted columns, a trigger, and an audit table and its working fine there and I cant find any differences in the table/sproc/trigger that would allow it to work there and not here. I've pretty much exhausted the other posts and blogs I can find.
Fixed! Needed to update the app code to specify the data type and length:
parameters.Add("#City", organizationAddress.City, System.Data.DbType.AnsiString, System.Data.ParameterDirection.Input, 60);
where it was previously just:
parameters.Add("#City", organizationAddress.City)
This at least get the app to run the sproc but still cant run it from SSMS via EXEC

SQL Server stored procedure optimal query method?

I have 5 meta tables that have the same format but depend on 5 other tables. Each Meta table looks like this:
CREATE TABLE [dbo].[SiteMetas]
(
[SiteMetaId] [bigint] IDENTITY(1,1) NOT NULL,
[SiteId] [bigint] NOT NULL,
FOREIGN KEY([SiteId]) REFERENCES [dbo].[Sites] ([SiteId]),
[MetaGroup] [nvarchar] (64) NOT NULL,
[MetaName] [nvarchar] (128) NOT NULL,
[MetaType] [char] NOT NULL DEFAULT 0, -- t, i, r, d, s, b
[MetaBool] [bit] DEFAULT NULL, -- t
[MetaInteger] [bigint] DEFAULT NULL, -- i
[MetaReal] [real] DEFAULT NULL, -- r
[MetaDateTime] [datetime] DEFAULT NULL, -- d
[MetaString] [nvarchar] (MAX) DEFAULT NULL, -- s
[MetaBinary] [varbinary] (MAX) DEFAULT NULL, -- b
[MetaCreated] [datetime] NOT NULL DEFAULT (GETUTCDATE()),
[MetaExpires] [datetime] DEFAULT NULL,
[MetaUpdated] [datetime] DEFAULT NULL,
PRIMARY KEY CLUSTERED ([SiteMetaId] ASC) WITH (IGNORE_DUP_KEY = ON),
UNIQUE NONCLUSTERED ([SiteId] ASC, [MetaGroup] ASC, [MetaName] ASC) WITH (IGNORE_DUP_KEY = ON)
);
This is for Site but there's 4 more. Like Users, ...
And I want to read the Binary meta value from Site. So wrote this stored procedure:
CREATE PROCEDURE [dbo].[GetSiteMetaBinary]
#SiteId AS bigint,
#Group AS nvarchar(64),
#Name AS nvarchar(128)
AS
BEGIN
SELECT TOP 1 [MetaBinary]
FROM [dbo].[SiteMetas]
WHERE [SiteId] = #SiteId
AND [MetaGroup] = #Group
AND [MetaName] = #Name
AND [MetaType] = 'b';
END;
This stored procedure has duplicates for User too... and the rest of the tables. That just replaces Site with User in its body.
But thinking that I have too many of these I wrote this one:
CREATE PROCEDURE [dbo].[GetMeta]
#Set AS nvarchar(64),
#Id AS bigint,
#Group AS nvarchar(64),
#Name AS nvarchar(128),
#Type AS nvarchar(16)
AS
BEGIN
DECLARE #Flag nchar(1);
DECLARE #Sql nvarchar(MAX);
SET #Flag = CASE #Type
WHEN 'Bool' THEN 't'
WHEN 'Integer' THEN 'i'
WHEN 'Real' THEN 'r'
WHEN 'DateTime' THEN 'd'
WHEN 'String' THEN 's'
WHEN 'Binary' THEN 'b'
ELSE NULL
END;
SET #Sql = N'SELECT TOP 1 [Meta' + #Type + N'] FROM [dbo].[' + #Set + N'Metas]' +
N'WHERE [' + #Set + N'Id] = #Id AND [MetaGroup] = #Group AND [MetaName] = #Name AND [MetaType] = #Flag;';
-- SELECT #Sql; -- DEBUG
EXEC sp_executesql #Sql,
N' #Id AS bigint, #Group AS nvarchar(64), #Name AS nvarchar(128), #Flag AS nchar(1)',
#Id, #Group, #Name, #Flag
;
END;
which is a general use stored procedure to read any data typed stored in a column based on input arguments. I use it like this [dbo].[GetMeta] 'Site', 1, 'group', 'name', 'Binary' the difference being that the actual query is dynamically generated so it's not known before hand by SQL Server like the first specialized variant.
Which of the two choices is better from a performance point of view and friendlier to SQL Server's internals? A dedicated one for each table and column data type of a general one that internally builds a query based on fed arguments.
I can use either. I like the last as it does not pollute my stored procedure space. :) The first one is more clear and SQL Server might be able to optimize it better. Not sure...
PS: I'm quite new to SQL Server
Static procedures are usually faster because the SQL engine can cache the compiled SP's execution plan.
However, unless this SP will be called a lot or is time-critical, it probably isn't worth worrying about it because the time savings of only having to maintain one SP will make up for the very small amount of time difference spent waiting for the SP to finish.

Resources