How put query with openquery faster? - sql-server

this query takes a lot of time running.
Does anyone know anyway to put them faster?
DECLARE #actual as INT
DECLARE #expected as INT
DECLARE #ID as INT
DECLARE #TSQL varchar(100)
DECLARE #t table (c int)
DECLARE #Id_aux varchar(100)
SET #ID = (select max(id) from [STG].table where fk_country = 5 )
SET #Id_aux= CONVERT(NVARCHAR(100), #ID)
SET #TSQL = 'SELECT * FROM OPENQUERY([MA],''select count(*) from table where id <=' + #Id_aux + ''')'
INSERT INTO #t EXEC (#TSQL)
SET #expected = (select c from #t)
SET #actual = (SELECT count(*) from [STG].table where fk_country = 5 and id<=#ID)
select #expected
select #actual
Thanks

It is better to use a different approach while executing queries via linked servers.
EXECUTE ... AT [Linked_Server];
Benefits:
This way a query will be executed on the remote server. Guaranteed. Thus it will be much more performant.
Very easy to pass parameters. No strings concatenation.
Useful link: SQL Server: Execute At LinkedServer
Check it out below.
SQL
DECLARE #actual as INT;
DECLARE #expected as INT;
DECLARE #ID as INT;
DECLARE #TSQL varchar(100);
DECLARE #t table (c int);
--DECLARE #Id_aux varchar(100);
SET #ID = (select max(id) from [PAY_STG].pay.paybuddy_purchase_ex where fk_country = 5 );
--SET #Id_aux= CONVERT(NVARCHAR(100), #ID);
--SET #TSQL = 'SELECT *
-- FROM OPENQUERY([BE_PAY_MA],
-- ''SELECT COUNT(*) FROM paybuddy_purchase_ex WHERE id <=' + #Id_aux + ''')';
--INSERT INTO #t EXEC (#TSQL);
INSERT INTO #t
EXECUTE(N'SELECT COUNT(*) FROM paybuddy_purchase_ex WHERE id <= ?',
#ID) AT [BE_PAY_MA];
SET #expected = (select c from #t);
SET #actual = (SELECT count(*) from [PAY_STG].pay.paybuddy_purchase_ex where fk_country = 5 and id<=#ID);
select #expected;
select #actual;

Related

SQL - Iterate over a temp table to check for free DBs names

I'm trying to do an SQL script here but facing some dificulties as I don't have so much knowledges on that, here is my issue:
I need to create a temp table with pre-determined values (dbrsm01 to dbrsm30) and check iterating through these values if one of them is available on MSSQL server to be used as a new database, if one of these values is already in use it needs to be ignored as I need to create a new DB using this value.
Here is what I've done so far:
DECLARE #temp TABLE (id int, dbname varchar(10))
INSERT INTO #temp VALUES(1,'dbrsm01');
INSERT INTO #temp VALUES(2,'dbrsm02');
INSERT INTO #temp VALUES(3,'dbrsm03');
...
INSERT INTO #temp VALUES(27,'dbrsm27');
INSERT INTO #temp VALUES(28,'dbrsm28');
INSERT INTO #temp VALUES(29,'dbrsm29');
INSERT INTO #temp VALUES(30,'dbrsm30');
DECLARE #maxid INT, #counter INT, #tempname VARCHAR(10), #nameset VARCHAR(10)
SET #counter = 1
SELECT #maxid = COUNT(*) FROM #temp
WHILE (#counter <= #maxid OR #nameset = #tempname)
BEGIN
SET #tempname = (SELECT dbname FROM #temp WHERE id = #counter)
IF NOT EXISTS (SELECT name FROM sys.databases WHERE name = #tempname)
BEGIN
SET #nameset = #tempname
END
SET #counter = #counter + 1
END
SELECT #nameset as [#dbname]
Any help on that is appreciate! Thank you all!
This doesn't need to be iterative at all. You can do this in a single statement:
WITH Tally AS (
SELECT 1 AS i
UNION ALL
SELECT i + 1
FROM Tally
WHERE i + 1 <= 30)
SELECT 'dbrsm' + CONVERT(varchar(7),T.i)
FROM Tally T
LEFT JOIN sys.databases d ON 'dbrsm' + CONVERT(varchar(7),T.i) = d.[name]
WHERE d.database_id IS NULL;
Edit: A mindset you need to change when writing SQL is thinking programmatically. You don't want to think about what you're going to do to a row, you need to think about what you're going to do to a column. Using loops are not a way of thinking in a dataset approach (normally).
Edit: Nevermind, here's how to make a CREATE statement and make all the databases:
DECLARE #SQL nvarchar(MAX);
WITH Tally AS (
SELECT 1 AS i
UNION ALL
SELECT i + 1
FROM Tally
WHERE i + 1 <= 30)
SELECT #SQL = STUFF((SELECT NCHAR(10) + N'CREATE DATABASE ' + QUOTENAME(N'dbrsm' + CONVERT(varchar(7),T.i)) + N';'
FROM Tally T
LEFT JOIN sys.databases d ON 'dbrsm' + CONVERT(varchar(7),T.i) = d.[name]
WHERE d.database_id IS NULL
FOR XML PATH ('')),1,1,'');
PRINT #SQL; --This is your best friend for troubleshooting
--EXEC sp_executesql #SQL; --Uncomment to run your dynamic SQL
You could use dynamic SQL:
DECLARE #sql NVARCHAR(MAX)
,#maxid INT = (SELECT COUNT(*) FROM #temp)
,#counter INT = 1
,#tempname VARCHAR(10)
,#nameset VARCHAR(10);
WHILE (#counter <= #maxid)
BEGIN
SET #tempname = (SELECT dbname FROM #temp WHERE id = #counter);
IF NOT EXISTS (SELECT name FROM sys.databases WHERE name = #tempname)
BEGIN
SET #nameset = #tempname;
SET #sql = 'CREATE DATABASE ' + QUOTENAME(#nameset);
PRINT #sql;
EXEC (#sql);
END
SET #counter += 1;
END
You can use BREAK and do what you want, like this:
DECLARE #temp TABLE (id int, dbname varchar(10))
INSERT INTO #temp VALUES(1,'dbrsm01');
INSERT INTO #temp VALUES(2,'dbrsm02');
INSERT INTO #temp VALUES(3,'dbrsm03');
DECLARE #maxid INT, #counter INT, #tempname VARCHAR(10), #nameset VARCHAR(10)
SET #counter = 1
SELECT #maxid = COUNT(*) FROM #temp
WHILE (#counter <= #maxid /*OR #nameset = #tempname --YOU DON'T NEED THIS */)
BEGIN
SELECT #tempname = dbname FROM #temp WHERE id = #counter
PRINT #tempname
IF NOT EXISTS (SELECT name FROM sys.databases WHERE name = #tempname)
BEGIN
SET #nameset = #tempname
PRINT 'ITS FREE'
BREAK
END
ELSE
PRINT 'ITS IN USE!'
SET #counter = #counter + 1
END
SELECT #nameset as [#dbname]
If you need to create all DB's that dont exist, remove the BREAK and add your logic (CREATE DATABASE ...)
SELECT 'CREATE Database '+A.dbname+'
GO '
FROM
(
SELECT K.dbname
FROM #temp K
LEFT JOIN sys.databases Y
ON K.dbName = Y.Name
WHERE Y.name is NULL
)A
You can copy the result of this statement and run it.

Count all rows from necessary tables using cursor

I have few tables in my database and I want to count total rows of all those tables based on AppoitmentID for which I use a scalar cursor and a table variable to store some data of those tables based on AppoitmentID. After the end of cursor I count rows of table variable in which I had inserted data using dynamic query in cursor.
But it gives me the following error
Must declare the table variable "#ProcCount".
Is there any other way to get the count of all rows from necessary tables.
Below is my Code :
Create FUNCTION [dbo].[ufn_GetProcedureCount]
(
)
RETURNS INT
AS
BEGIN
-- Declare the return variable here
DECLARE #MenuID bigINT, #TableName VARCHAR(150);
DECLARE #Result int
DECLARE #ProcCount TABLE (AppID INT, WoundId bigINT,TableName varchar(150));
DECLARE #sql nvarchar(2000)
DECLARE #Count int
DECLARE Cur_PendSign Cursor For
select Distinct MenuID,TableName from AppointmentTypeRequiredDocumnet A inner join Menu M on M.ID =A.MenuID where m.MenuGroupID = 8
OPEN Cur_PendSign
FETCH Cur_PendSign INTO #MenuID, #TableName
WHILE ##FETCH_STATUS=0
BEGIN
SET #sql='DECLARE #ProcCount TABLE (AppID INT, WoundId bigINT,TableName varchar(150))'
SET #sql=#sql+'INSERT INTO #ProcCount (AppID,WoundId)
SELECT TOP 1 V.AppointmentID, 1
FROM ['+#TableName+'] V WITH(NOLOCK)'
set #sql=#sql+ 'select count(*) from #ProcCount;'
--set #sql=#sql+ 'DECLARE #Count int'
EXECUTE sp_executesql #sql
FETCH Cur_PendSign INTO #MenuID, #TableName
END
CLOSE Cur_PendSign
DEALLOCATE Cur_PendSign
--set #Result = select count(*) from #ProcCount
RETURN #Result
END
There are two issues with the script.
First: Missing where clause
select Distinct MenuID,TableName from Appointment A.AppointmentID = 8
Second: you need to create persistent table rather than variable table due to limitation of scope as you cant declare variable table outside of EXEC and use it.
This query should work for you. You need to use sp_executesql to execute dynamic queries.
CREATE FUNCTION [dbo].[ufn_GetProcedureCount]
(
)
RETURNS INT
AS
BEGIN
-- Declare the return variable here
DECLARE #Result INT
DECLARE #ProcCount TABLE (
AppID INT,
WoundId BIGINT,
TableName VARCHAR(150)
)
DECLARE #MenuID BIGINT,
#TableName VARCHAR(150)
--Get all table which I need to count rows
DECLARE Cur_PendSign CURSOR FOR
SELECT DISTINCT MenuID, TableName FROM Appointment WHERE AppointmentID = 8
OPEN Cur_PendSign
FETCH Cur_PendSign INTO #MenuID, #TableName
WHILE ##fetch_status = 0
BEGIN
-- Insert require data into #ProcCount using dynamic query
DECLARE #query VARCHAR(255) = 'INSERT INTO #ProcCount (AppID,WoundId,TableName)
SELECT TOP 1 AppointmentID, WoundId, TableName
FROM [' + #TableName + '] WITH(NOLOCK) '
EXECUTE sys.sp_executesql #query
FETCH Cur_PendSign INTO #MenuID, #TableName
END
CLOSE Cur_PendSign
DEALLOCATE Cur_PendSign
--Get Count of all rows from tables
SELECT #Result = COUNT(*) FROM #ProcCount
RETURN #Result
END
Without having a cursor, Loops you can do it with Dynamic coding..
Schema:
(It may be differ with your actual schema)
CREATE TABLE #Appointment (MENUID INT IDENTITY,AppointmentID INT, TableName VARCHAR(20))
INSERT INTO #Appointment
SELECT 1,'TABLE1'
UNION ALL
SELECT 2, 'TABLE2'
UNION ALL
SELECT 8,'TABLE3'
UNION ALL
SELECT 8,'TABLE4'
UNION ALL
SELECT 8,'TABLE5'
Now do like below
DECLARE #QRY VARCHAR(MAX)='';
SELECT #QRY = #QRY+ 'SELECT COUNT(1) AS COUNT_TABLES FROM '+ TableName + ' (NOLOCK)
UNION ALL
' FROM (
SELECT DISTINCT TableName FROM #Appointment A WHERE A.AppointmentID = 8
)A
SELECT #QRY = SUBSTRING(#QRY,1,LEN(#QRY)-11)
SELECT #QRY = '
SELECT SUM(COUNT_TABLES) FROM (
' + #QRY+'
)A'
--PRINT #QRY
EXEC (#QRY)
If you want to check what #QRY contains
/*
SELECT SUM(COUNT_TABLES) FROM (
SELECT COUNT(1) AS COUNT_TABLES FROM TABLE3 (NOLOCK)
UNION ALL
SELECT COUNT(1) AS COUNT_TABLES FROM TABLE4 (NOLOCK)
UNION ALL
SELECT COUNT(1) AS COUNT_TABLES FROM TABLE5 (NOLOCK)
)A
*/

using ##ROWCOUNT in Dynamic SQL

I am using Dynamic SQL to retrieve datasets from multiple tables in order to monitor our daily data extraction from the iSeries system.
I have the below dynamic SQL code which works fine, but I want to only run the data to get each tables records if data has been extracted for the day
-- Create a table variable to store user data
DECLARE #myTable TABLE
(
docID INT IDENTITY(1,1),
docRef VARCHAR(50),
letterDir VARCHAR(500)
);
insert #myTable select docRef, saveDir from alpsMaster.dbo.uConfigData
-- Get the number of rows in the looping table
DECLARE #RowCount INT, #SQL nvarchar(500), #LoopSQL nvarchar(2000), #Date varchar(20)
set #Date='29 Oct 2013'
SET #RowCount = (SELECT COUNT(docID) FROM #myTable)
-- Declare an iterator
DECLARE #I INT
-- Initialize the iterator
SET #I = 1
-- Loop through the rows of a table #myTable
WHILE (#I <= #RowCount)
BEGIN
-- Declare variables to hold the data which we get after looping each record
DECLARE #docRef VARCHAR(10), #saveDir VARCHAR(500)
-- Get the data from table and set to variables
SELECT #docRef = docref FROM #myTable WHERE docID = #I
SELECT #saveDir = letterDir FROM #myTable WHERE docID = #I
-- Display the looped data
--PRINT 'Row No = ' + CONVERT(VARCHAR(2), #I) + '; docRef = ' + #docRef
select #LoopSQL='
use alpsProduction;
declare #SQL nvarchar(500);
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(''[dbo].['+#docRef+']''))
begin
if exists(select * from sys.columns
where Name = ''YPTMPID'' and Object_ID = OBJECT_ID(''[dbo].['+#docRef+']''))
begin
set #SQL=''SELECT t.template_name,'''''+#saveDir+''''', Y.*
FROM [alpsProduction].[dbo].'+#docRef+' Y, alpsMaster.dbo.uDocumentTemplates t
where DTEINP='''''+#Date+''''' and t.template_Id=y.YPTMPID and t.docRef='''''+#docRef+'''''''
exec sp_executesql #SQL
end
end
'
--print #LoopSQL
exec sp_executesql #LoopSQL
-- Increment the iterator
SET #I = #I + 1
END
so I tried using
IF ##ROWCOUNT >0
Begin
exec sp_executesql #SQL
end
but it seems to never populate the ##Rowcount.
Whats the best way to only run that statement (exec sp_executesql #SQL) if the current table (identified by #docRef) has records in it for todays date (in the format dd mmm yyyy)
Create job to execute a sql script in which u must check inserted data on current day then execute your sp. like this.
IF EXISTS ( SELECT * FROM #TABLE T WHERE DATEDIFF(DD, GETUTCDATE(), T.CREATEDON) = 0 )
BEGIN
EXEC SP_EXECUTESQL #SQL
END

GENERIC STORED PROCEDURE TO AUDIT TABLE UPDATE

Is there a generic stored procedure to audit the table. I have actually made one but I don't think its efficient and the stored procedure is quite long. If someone knows a better way to do it then please help me out...!
This is my table trigger.
ALTER TRIGGER [dbo].[Trigger3]
ON [dbo].[UserInfo]
AFTER Update
AS
BEGIN
SET NOCOUNT ON;
DECLARE #TABLENAME VARCHAR(50)
DECLARE #var varbinary
SELECT * INTO #TEMPINSERTED FROM inserted
SELECT * INTO #TEMPDELETED FROM deleted
SET #var = COLUMNS_UPDATED()
EXEC TetsProc #TEMPINSERTED, #TEMPDELETED, ##PROCID, #var
DROP TABLE #TEMPINSERTED
DROP TABLE #TEMPDELETED
END
This is my stored procedure
ALTER PROCEDURE [dbo].[TetsProc]
( #insertTable varchar(max),
#deleteTable varchar(max),
#IDZ varchar(max),
#var1 varbinary
)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #TABLE AS TABLE (COL_NAME NVARCHAR(MAX))
DECLARE #idTable INT
SELECT #idTable = T.id
FROM sysobjects P JOIN sysobjects T ON P.parent_obj = T.id
WHERE P.id = #IDZ
declare #q1 nvarchar(max),#q2 nvarchar(max)
set #q1 = 'select * from ' + #insertTable
set #q2 = 'select * from ' + #deleteTable
DECLARE #TABLENAME NVARCHAR(250)
SELECT #TABLENAME = OBJECT_NAME(parent_obj)
FROM sysobjects WHERE ID = #IDZ
----RETURN COLUMNS IF THEY ARE UPDATED----
SELECT #idTable = T.id
FROM sysobjects P JOIN sysobjects T ON P.parent_obj = T.id
WHERE P.id = ##procid
DECLARE #Columns_UpdateD VARCHAR(50)
SELECT #Columns_Update = ISNULL(#Columns_Updated + ', ', '') + name
FROM syscolumns
WHERE id = #idTable
AND CONVERT(VARBINARY,REVERSE(#var1)) & POWER(CONVERT(BIGINT, 2), colorder - 1) > 0
select status into #TmpcolumnsUpdated from dbo.ParseByComma(#Columns_UpdateD)
DECLARE #QRY1 NVARCHAR(MAX)
DECLARE #QRY2 NVARCHAR(MAX)
declare #column_name varchar(50)
DECLARE cursorColumnName CURSOR FOR
select status from #TmpcolumnsUpdated
OPEN cursorColumnName
FETCH NEXT FROM cursorColumnName INTO #column_name
WHILE ##FETCH_STATUS = 0
BEGIN
SET #QRY1= 'SELECT '+#column_name + ' FROM '+ #insertTable
SET #QRY2= 'SELECT '+#column_name + ' FROM ' + #deleteTable
DECLARE #tab AS TABLE (OLD_COL VARCHAR(10))
DECLARE #tab1 AS TABLE (NEW_COL VARCHAR(10))
INSERT into #tab EXECUTE sp_executesql #QRY2
INSERT into #tab1 EXECUTE sp_executesql #QRY1
DECLARE #OLD_VALUE VARCHAR(MAX)=(SELECT OLD_COL FROM #tab)
DECLARE #NEW_VALUE VARCHAR(MAX)=(SELECT NEW_COL FROM #tab1)
IF(#OLD_VALUE!=#NEW_VALUE)
BEGIN
INSERT INTO UpdateInfo (Table_Name,Col_Name,Old_Value,New_Value,Time)
(
SELECT
#TABLENAME,
#column_name,
#OLD_VALUE,
#NEW_VALUE,
GETDATE()
)
SELECT * FROM UpdateInfo
END
DELETE FROM #tab
DELETE FROM #tab1
FETCH NEXT FROM cursorColumnName INTO #column_name
END
CLOSE cursorColumnName
DEALLOCATE cursorColumnName
drop table #TmpcolumnsUpdated
END
Looks like a sure way to drag down the performance of your server.
If you are using SQL Server 2012, I'd recommend you to check out Sql Server Audit instead.
If you have to stick with your mechanism, at the very least lose the cursor in the PROC.

How can we use EXEC sp_executesql for two dynamic SQL statements?

tables:
create table TabA
(ID int, Name varchar(20))
insert into TabA
select 1,'ABC' union
select 2,'DEF' union
select 3,'GHD'
create table TabB
(ID int, Name varchar(20))
insert into TabA
select 1,'XYZ' union
select 2,'STF' union
select 3,'LDZ'
create table status
(Result1 int,Result2 int )
Create table query(query1 varchar(1000),query2 varchar(1000))
Insert into query(query1,query2)
select '''select COUNT(*) from TabA''','''select COUNT(* )from TabB'''
select * from query
procedure:
create Procedure [dbo].spStatus
AS
BEGIN
SET NOCOUNT ON;
Declare #sqlString1 nvarchar(1000)
,#sqlString2 nvarchar(1000)
,#col_value1 varchar(256)
,#col_value2 varchar(256)
select #sqlString1 = query1
, #sqlString2 =query2
from Query
EXEC sp_executesql
#query=#sqlString1, --sql string is your full select statement
#params = N'#col_Value1 varchar(256) OUTPUT',
#col_Value1 = #col_Value1 OUTPUT
print(#sqlString1)
-- #sqlString2, --sql string is your full select statement
--#params = N'#col_Value2 varchar(256) OUTPUT',
-- #col_Value2 = #col_Value2 OUTPUT
Insert Into dbo.Status(Result1,Result2 )
Values(#col_Value1,#col_Value2)
End
It works if we use #query=#sqlString1 only but I want both statement #query=#sqlString1,#query=#sqlString2 should execute together.
Please help how can we use both statement to execute?
Thanks in Advance
Did you mean:
SET #sqlString1 = #sqlString1 + ';' + #sqlString2;
EXEC sp_executesql #query = #sqlString1 --...
Concat the two queries together with a + (#query=#sqlString1 + '; ' + #sqlString2)
Then use two variables to capture the two counts into output variables
OR
Insert into query(query1,query2)
EXEC sp_executesql 'SELECT ( select COUNT(*) from TabA ) AS query1, ( select COUNT(*)from TabB ) AS query2'
... but really and truly dynamic SQL isn't needed for that at all.
try this:
--add this
DECLARE #SQL nvarchar(max)
SET #SQL=ISNULL(#sqlString1,'')+';'+ISNULL(#sqlString2,'')
--change this
EXEC sp_executesql #query=#SQL
,#params = N'#col_Value1 varchar(256) OUTPUT'
,#col_Value1 = #col_Value1 OUTPUT

Resources