Is it possible to execute a SQL statement Stored in a Table, with T-SQL?
DECLARE #Query text
SET #Query = (Select Query FROM SCM.dbo.CustomQuery)
The statements that are stored in the table are ad-hoc statements which could be SELECT TOP 100 * FROM ATable to more complex statements:
Select
J.JobName As Job,
JD.JobDetailJobStart AS StartDate,
JD.JobDetailJobEnd AS EndDate,
(
SELECT (DATEDIFF(dd, JD.JobDetailJobStart, JD.JobDetailJobEnd) + 1) -(DATEDIFF(wk, JD.JobDetailJobStart, JD.JobDetailJobEnd) * 2) -(CASE WHEN DATENAME(dw, JD.JobDetailJobStart) = 'Sunday' THEN -1 ELSE 0 END) -(CASE WHEN DATENAME(dw, JD.JobDetailJobEnd) = 'Saturday' THEN -1 ELSE 0 END)
) AS NumberOfWorkingDays,
JD.JobDetailDailyTarget AS DailyTarget,
JD.JobDetailWeeklyTarget AS WeeklyTarget,
JD.JobDetailRequiredQTY AS RequiredQuantity,
(
Select SUM(sJL.JobLabourQuantityEmployees) From JobLabour sJL
) AS NumberOfEmployees,
(
Select
SUM((sEM.EmployeeDesignationDefaultRate * sJL.JobLabourQuantityEmployees)*8)*(SELECT (DATEDIFF(dd, JD.JobDetailJobStart, JD.JobDetailJobEnd) + 1) -(DATEDIFF(wk, JD.JobDetailJobStart, JD.JobDetailJobEnd) * 2) -(CASE WHEN DATENAME(dw, JD.JobDetailJobStart) = 'Sunday' THEN -1 ELSE 0 END) -(CASE WHEN DATENAME(dw, JD.JobDetailJobEnd) = 'Saturday' THEN -1 ELSE 0 END))
from EmployeeDesignation sEM
Inner join JobLabour sJL on sJL.EmployeeDesignationID = sEM.EmployeeDesignationID
) AS FullEmployeeRate
from Job J
Inner Join JobDetail JD on JD.JobID = J.JobID
Inner Join JobLabour JL on JL.JobID = J.JobID
WHERE J.JobActive = 0
I want to execute the #Query Variable that I declared from T-SQL. Is this possible? (I am running a MSSQL 2005 enviroment)
You can use
EXECUTE sp_executesql #Query
to run your T-SQL
Here's a link to the MS docn for SQL Server 2005
http://msdn.microsoft.com/en-us/library/ms188001%28v=sql.90%29.aspx
The previous answer allows you to run one statement, and is valid. The question was on how to run SQL Statements stored in a table, which I took as more than one statement being executed. For this extra step, there is a while loop involved to iterate through each statement that need to be run.
-- Author: Chad Slagle
DECLARE #Table table (RID BIGINT IDENTITY(1,1) PRIMARY KEY CLUSTERED,
SQLText NVARCHAR(MAX) )
DECLARE #StatementMax INT
,#statementMin INT
,#isTest TINYINT = 1
,#SQLStatement NVARCHAR(MAX)
-- Insert SQL Into Temp Table
INSERT INTO #table (SQLText)
VALUES ('SELECT ##Version');
INSERT INTO #table (SQLText)
VALUES ('SELECT SERVERPROPERTY(''ProductVersion'')')
-- Get your Iterator Values
SELECT #statementMAX = MAX(RID), #statementMIN = MIN(RID) FROM #table
IF #isTest = 1 BEGIN SELECT *, #statementMax AS MaxVal, #StatementMin AS MinVal FROM #Table END
-- Start the Loop
WHILE #StatementMax >= #statementMin
BEGIN
SELECT #SQLStatement = SQLText FROM #table WHERE RID = #statementMin -- Get the SQL from the table
IF #isTest = 1 BEGIN SELECT 'I am executing: ' + #SQLStatement AS theSqlBeingRun, GETDATE(), #statementMin, #StatementMax END
ELSE
BEGIN
EXECUTE sp_ExecuteSQL #SQLStatement -- Execute the SQL
END
DELETE FROM #table WHERE RID = #statementMin -- Delete the statement just run from the table
SELECT #statementMIN = MIN(RID) FROM #Table -- Update to the next RID
IF #isTest = 1 BEGIN SELECT * FROM #table END
END
In Summary, I created a temp table and put some SQL in it, using a IDENTITY (RID) field to provide an iterator for the while loop. Then ran the while loop. In the example, you should return two views of your SQL Version. I built this on 2k8, and I hope it helps someone out of a jam one day..
We use a much simpler approach. Store the scripts (raw sql or stored procedure calls) in a table with a column containing an identifying code for said script. Use placeholders in your script for parameters. Any scripts that are used a lot can be "keyed" in your app or web config file. If scripts need to be executed in a specific order, put an ordinal column in the table. The actual "script" can then be pulled into a c# list or array, passed to a database class library and executed accordingly. This gives you dynamic control over your SQL and allows you to make changes on the database side for said scripts without recompiling your main application.
TRUNCATE TABLE AllTableUnion
DECLARE #Query2 Nvarchar(MAX)
SET #Query2='SELECT * FROM #UnionString t1)A'
INSERT INTO AllTableUnion
EXEC(#Query2)
DECLARE #Query4 Nvarchar(MAX)=(SELECT Query FROM AllTableUnion)
EXECUTE sp_ExecuteSQL #Query4
I have a similar situation I just can't get to work. I'd like to create a table, where we have a customer number, and the business rules used to determine codes for a variety of items. The table psudo looks like this:
| CustomerNumber | RuleName | Rule |
----------------------------------------------------------------------
| 12345 |ShippingCharged | iif(id.ItemID in (1,2,3,4,5,6) AND
cod.Code IN (5,6,7,8),1,0)
| 99999 |ShippingCharged | iif(id.ItemID in (1,2,3,7,9,10) AND
cod.Code NOT IN (5,7,8),1,0)
I want to run a SELECT in the form:
SELECT CustomerNumber, RuleName, Rule as Value
FROM CustomerRules cr
JOIN CustomerData cd
ON cd.CustomerNumber = cd.CustomerNumber
JOIN ItemsData id
ON cd.ItemID = id.ItemID
JOIN CodesData cod
ON cd.Code = cod.Code
WHERE cr.RuleName = 'ShippingCharged'
To return The Customer Number, the name of the Rule used and the calculated value of the IIF statement. I am getting the text of the Rule calculation, rather than the calculated value.
I've tried various forms of DSQL and TSQL but can't seem to get the column to be treated as a function, just a text value from the table.
Any ideas?
Related
I tried to execute this code and it worked o.k without the the stored procedure and with it, it made an error.
The error is:
Msg 102, Level 15, State 1, Procedure UV_MTBF, Line 251
Incorrect syntax near 'Event_'.
Is the stored procedure have a limitation in length?
can someone help me with my code?
edit*
my problem is with ' + QUOTENAME(#category,N'''') + N'
i want to add an integer from a variable that i received in the stored procedure. how can i do it?
enter code here:
CREATE PROCEDURE dbo.MTBFCalculation #Category int, #Action bit, #relateToParent bit
as
IF EXISTS (SELECT 1 FROM sys.objects WHERE [name] = '[dbo].[UV_MTBF]')
DROP VIEW [dbo].[UV_MTBF];
DECLARE #Event nvarchar(MAX) = N'
CREATE VIEW [dbo].[UV_MTBF]
as
with failureReportTable as (SELECT [ID] as failure_id
,[Login_ID]
,[Event_ID]
,[StartDate]
,[EndDate]
,DATEDIFF(Hour,[StartDate],[EndDate]) as eventDurationMin
,[IsRelevantForBI]
,[IsParallelReport]
,[ParentReportID]
,[IsPausedEvent]
,Case
When ParentReportID>0 Then 1 --Chiled
When IsParallelReport=1 Then 2 --Parent
Else 3 --not Parallel
End as ParallelStatus
FROM [TDM_Analysis].[dbo].[FailureReports]),
fullFailure as (select *, ROW_NUMBER() OVER (ORDER BY [StartDate] ) AS IDrow
from failureReportTable join [TDM_Analysis].[dbo].[UV_filteredLogins] as viewLogins on failureReportTable.Login_ID=viewLogins.ID
WHERE event_id IN (SELECT ID FROM [TDM_Analysis].[dbo].[Events] where EventCategory_ID=' + QUOTENAME(#category,N'''') + N')
and (ParallelStatus=3 or ParallelStatus=(case when ' + QUOTENAME(#relateToParent,N'''') + N'=1 then 2 else 1 end))),
--------------create first failure table------------------
failure_Event_1 as (select f1.failure_id as Event_1_Failure_ID
,f1.[Login_ID] as Event_1_Login_ID
,f1.[Event_ID] as Event_1_Event_ID
,f1.[StartDate] as Event_1_StartDate
,f1.[EndDate] as Event_1_EndDate
,f1.eventDurationMin as Event_1_eventDurationMin
--,f1.[IsRelevantForBI] as Event_1_IsRelevantForBI
--,f1.[IsParallelReport] as Event_1_IsParallelReport
-- ,f1.[ParentReportID] as Event_1_ParentReportID
-- ,f1.[IsPausedEvent] as Event_1_IsPausedEvent
,f1.[Test_Name] as Event_1_TestName
,f1.Phase_Name as Event_1_PhaseName
,f1.PressName as Event_1_PressName
,f1.PressType as Event_1_PressType
--,f1.[Operator] as Event_1_Operator
,f1.[LoginDate] as Event_1_LoginDate
,f1.[LogoutDate] as Event_1_LogoutDate
,f1.TimeDiff as Event_1_LoginDuration
,f1.IDrow+1 as row1
from fullFailure as f1),
--------------create second failure table------------------
failure_Event_2 as (select f1.failure_id as Event_2_Failure_ID
,f1.[Login_ID] as Event_2_Login_ID
,f1.[Event_ID] as Event_2_Event_ID
,f1.[StartDate] as Event_2_StartDate
,f1.[EndDate] as Event_2_EndDate
,f1.eventDurationMin as Event_2_eventDurationMin
-- ,f1.[IsRelevantForBI] as Event_2_IsRelevantForBI
-- ,f1.[IsParallelReport] as Event_2_IsParallelReport
-- ,f1.[ParentReportID] as Event_2_ParentReportID
-- ,f1.[IsPausedEvent] as Event_2_IsPausedEvent
,f1.[Test_Name] as Event_2_TestName
,f1.Phase_Name as Event_2_PhaseName
,f1.PressName as Event_2_PressName
,f1.PressType as Event_2_PressType
-- ,f1.[Operator] as Event_2_Operator
,f1.[LoginDate] as Event_2_LoginDate
,f1.[LogoutDate] as Event_2_LogoutDate
,f1.TimeDiff as Event_2_LoginDuration
,f1.IDrow as row2
from fullFailure as f1),
------------- join two failure tabels and calculating MTTR-mean time to repair (duration of failue), MTTF-mean time to failue( end of one until start of a new one), MTBF-mean time between failue (from start of a failure to start of a new one)--------------------
joinFailures as (select *, Event_1_eventDurationMin as MTTR
,CASE
When isnull(f2.row2,0)=0 then DATEDIFF(HOUR,f1.Event_1_EndDate,f1.Event_1_LogoutDate)
WHEN f1.Event_1_Login_ID=f2.Event_2_Login_ID THEN DATEDIFF(HOUR,f1.Event_1_EndDate,f2.Event_2_StartDate)
When (select TOP 1 sum(timediff)
from [TDM_Analysis].[dbo].[UV_filteredLogins]
where logindate>f1.Event_1_LogoutDate and logindate<f2.Event_2_LoginDate) is null then DATEDIFF(HOUR,f1.Event_1_EndDate,f1.Event_1_LogoutDate)+DATEDIFF(HOUR,f2.Event_2_LoginDate, f2.Event_2_StartDate)
ELSE
(select TOP 1 sum(timediff)+DATEDIFF(HOUR,f1.Event_1_EndDate,f1.Event_1_LogoutDate)+DATEDIFF(HOUR,f2.Event_2_LoginDate, f2.Event_2_StartDate)
from [TDM_Analysis].[dbo].[UV_filteredLogins]
where logindate>f1.Event_1_LogoutDate and logindate<f2.Event_2_LoginDate)
END AS MTTF
from failure_Event_1 as f1 left join failure_Event_2 as f2 on f1.row1=f2.row2),
positiveJoinFailure as (select * from joinFailures where MTTF>=0)
---- select calculation table order by ascending time----------
select * --Event_1_Failure_ID,Event_2_Failure_ID,MTTR,MTTF, MTTR+MTTF as MTFB
from positiveJoinFailure
--order by row1
';
--------------------------------------------------------Action------------------------------------------------------------------------------
if #Action=1
begin
EXEC sp_executesql #Event;
end
for this part of your query
where EventCategory_ID=' + QUOTENAME(#category,N'''') + N')
2 option here, you convert the value of #category to string and then concatenate with the dynamic query
where EventCategory_ID=' + convert(varchar(10), #category)
OR, you pass the value in as a parameter.
for this option, you specify #category in the dynamic query
where EventCategory_ID= #category
and (ParallelStatus=3 ....
and you pass the value in at sp_executesql
EXEC sp_executesql #Event, N'#category int', #category
By the way, Option 2 is the preferred method when using dynamic query
I have a trigger in a table which keeps all the changes (Insert, Update, Delete). When I insert only one row per time it works fine. But when I am trying to insert multiple rows at once I receive this error :
Subquery returned more than 1 value. This is not permitted when the
subquery follows =, !=, <, <= , >, >= or when the subquery is used as
an expression.
Here is the code of the trigger ( I removed some parts that are not needed to shorten the code like variable declarations etc.)
UPDATE: The actual error is in these lines when #tempTrigT contains more than one rows:
Select * into #tempTrigT from (select * from deleted where #Action in ( 'U','D')) A UNION (select * from inserted where #Action ='I')
set #sql = 'set #audit_oldvalue=(select cast([' +#Item +'] as NVARCHAR(4000)) from #tempTrigT)';
EXEC SP_EXECUTESQL #sql,N'#audit_oldvalue sql_variant OUTPUT',#audit_oldvalue OUTPUT -- If inserted #audit_oldvalue gets the new value
set #sql = 'set #audit_value=(select cast(i.[' +#Item +'] as NVARCHAR(4000)) from dbo.TForms i inner join #tempTrigT d on i.id = d.id)';
EXEC SP_EXECUTESQL #sql,N'#audit_value sql_variant OUTPUT',#audit_value OUTPUT
How I can change it to work for multiple rows as well?
You are missing a row identifier so that you are only handling one row per loop. Something like:
DECLARE #ID int = (SELECT MIN(id) FROM #tempTrigT) to define a row at the start of your loop
WHERE id = #ID to filter to this row throughout the loop
DELETE FROM #tempTrigT WHERE id = #ID at the end of your loop, when that id is done processing
Then again, that may not even work if the id can repeat in #tempTrigT.
And with all that said...
I would definitely consider separating this into multiple triggers and save yourself the complexity you are facing by trying to loop through deleted or inserted records and handle them all accordingly. I would also consider simplifying your audit process. The end goal is to be able to look back at what records used to be, which you can achieve really simply:
INSERT INTO [dbo].[AuditTrailTForms] (TForms_Cols, ChangeDate, Change_User, Change_Type)
SELECT T.*, GETDATE(), COALESCE(ModifiedBy,suser_name()), 'Inserted'
FROM inserted i
JOIN TForms T on i.id = T.id
Then you can worry about making it easier to view which column values changed later on when you query these tables:
SELECT *
FROM (SELECT *, GETDATE(), 'Current', 'Current'
FROM TForms
WHERE ID = #AuditID
UNION ALL
SELECT *
FROM AuditTrailTForms
WHERE ID = #AuditID
--AND Change_Type =
--AND Change_User =
) T
ORDER BY ChangeDate DESC
Edit: Using an identity column:
You can use an identity column to define a row for each loop like so:
DECLARE #TotalRows int = (SELECT MAX(identityColumn) FROM #tempTrigT
DECLARE #RowID int = 1
WHILE #RowID <= #TotalRows
BEGIN
--Do stuff
--For Example
SET #sql = 'set #audit_oldvalue=(SELECT cast([' +#Item +'] as NVARCHAR(4000))
FROM #tempTrigT
WHERE T.IdentityColumn = #RowID)';
EXEC SP_EXECUTESQL #sql,N'#audit_oldvalue sql_variant OUTPUT',#audit_oldvalue OUTPUT
--then increment to the next row when you're done
SET #RowID = #RowID + 1
END
I have a select statement that involves a "IN" clause:
SELECT *
FROM [database]
WHERE [variablename] IN (1..2..3..4..)
Some of my variables are a letter followed by a number in sequential order. For example, V1, V2, V3....V30.
I would like to avoid typing every variable within the statement, is there a way to create an array or list that could store those 30 variables for me and then call them in the query? Or generate the variables by range?
Put the values in a table (could be a temp table) and join on that table.
declare #i int = 0
while #i < 30
begin
insert into tableName (columnName)
select 'V' + cast(#i as varchar)
select #i = #i + 1
end
select *
from [database] a inner join [database] b on a.columnName = b.columnName
Following is my Stored Proc.
ALTER PROCEDURE [GetHomePageObjectPageWise]
#PageIndex INT = 1
,#PageSize INT = 10
,#PageCount INT OUTPUT
,#AccountID INT
,#Interests Varchar(3000)
AS
BEGIN
SET NOCOUNT ON;
SELECT StoryID
, AlbumID
, StoryTitle
, CAST(NULL as varchar) AS AlbumName
, (SELECT URL FROM AlbumPictures WHERE (AlbumID = Stories.AlbumID) AND (AlbumCover = 'True')) AS AlbumCover
, Votes
, CAST(NULL as Int) AS PictureId
, 'stories' AS tableName
, (SELECT CASE WHEN EXISTS (
SELECT NestedStories.StoryID FROM NestedStories WHERE (StoryID = Stories.StoryID) AND (AccountID=#AccountID)
)
THEN CAST(1 AS BIT)
ELSE CAST(0 AS BIT) END) AS Flag
, (SELECT UserName FROM UserAccounts WHERE Stories.AccountID=UserAccounts.AccountID) AS Username
INTO #Results1
FROM Stories WHERE FREETEXT(Stories.Tags,#Interests) AND AccountID <> #AccountID AND IsActive='True' AND Abused < 10
I have 7 more SELECT Statements (not included in the question for brevity) in the Stored Proc similar to SELECT StoryID statement, which i UNION ALL like this
SELECT * INTO #Results9 FROM #Results1
UNION ALL
SELECT * FROM #Results2
UNION ALL
SELECT * FROM #Results3
UNION ALL
SELECT * FROM #Results4
UNION ALL
SELECT * FROM #Results5
UNION ALL
SELECT * FROM #Results6
UNION ALL
SELECT * FROM #Results7
UNION ALL
SELECT * FROM #Results8
SELECT ROW_NUMBER() OVER
(
ORDER BY [tableName] DESC
)AS RowNumber
, * INTO #Results
FROM #Results9
DECLARE #RecordCount INT
SELECT #RecordCount = COUNT(*) FROM #Results
SET #PageCount = CEILING(CAST(#RecordCount AS DECIMAL(10, 2)) / CAST(#PageSize AS DECIMAL(10, 2)))
SELECT * FROM #Results
WHERE RowNumber BETWEEN(#PageIndex -1) * #PageSize + 1 AND(((#PageIndex -1) * #PageSize + 1) + #PageSize) - 1
DROP TABLE #Results
DROP TABLE #Results1
DROP TABLE #Results2
DROP TABLE #Results3
DROP TABLE #Results4
END
This takes around 6 seconds to return the result. How can i improve this stored proc? I have very little knowledge about stored procedures.
Raise a nonclustered index on columns in where clause, IsActive, AccountID and Abused.
Well, you can only optimize it by getting rid of the temporary tables. Your approach sucks not because it is a stored procedure (so the SP part is simply totally irrelevant) but because you do a lot of temporary table stuff that forces linear execution and makes it hard for the query optimizer to find a better day to go forward.
In this particular case, it may be that your db design may be horrifically bad (why #result 1 to #result 8 to start with) and then you have tons of "copy into temp table" on every stored procedure.
Query Optimization in SQL works "statement by statement" and execution is never paralleled between statements - so the temp table stuff really gets into your way here. Get rid of the temp tables.
Never ever use directly SELECT * INTO #temp
INSTEAD
Always create #temp tables then INSERT INTO #temp
this will reduce query execution time by 70%
Though it might be frustration to create #temp table with exact structures,
so here is a short cut for that:This will be once performed
CREATE dbo.tableName by using SELECT * INTO tableName from Your calling query
then
sp_help TableName will provide structures.
Then create #temp table in Store Procedure.
I have optimized query for one of our client which was taking 45 minutes to execute, just replaced with this logic It worked !!!
Now it takes 5 Minutes !!
I have a need to grab data from multiple databases which has tables with the same schema. For this I created synonyms for this tables in the one of the databases. The number of databases will grow with time. So, the procedure, which will grab the data should be flexible. I wrote the following code snippet to resolve the problem:
WHILE #i < #count
BEGIN
SELECT #synonymName = [Name]
FROM Synonyms
WHERE [ID] = #i
SELECT #sql = 'SELECT TOP (1) *
FROM [dbo].[synonym' + #synonymName + '] as syn
WHERE [syn].[Id] = tr.[Id]
ORDER BY [syn].[System.ChangedDate] DESC'
INSERT INTO #tmp
SELECT col1, col2
FROM
(
SELECT * FROM TableThatHasRelatedDataFromAllTheSynonyms
WHERE [Date] > #dateFrom
) AS tr
OUTER APPLY (EXEC(#sql)) result
SET #i = #i + 1
END
I also appreciate for any ideas on how to simplify the solution.
Actually, it's better to import data from all tables into one table (maybe with additional column for source table name) and use it. Importing can be performed through SP or SSIS package.
Regarding initial question - you can achieve it through TVF wrapper for exec statement (with exec .. into inside it).
UPD: As noticed in the comments exec doesn't work inside TVF. So, if you really don't want to change DB structure and you need to use a lot of tables I suggest to:
OR select all data from synonym*** table into variables (as I see you select only one row) and use them
OR prepare dynamic SQL for complete statement (with insert, etc.) and use temporary table instead of table variable here.
My solution is quite simple. Just to put all the query to the string and exec it. Unfortunately it works 3 times slower than just copy/past the code for all the synonyms.
WHILE #i < #count
BEGIN
SELECT #synonymName = [Name]
FROM Synonyms
WHERE [ID] = #i
SELECT #sql = 'SELECT col1, col2
FROM
(
SELECT * FROM TableThatHasRelatedDataFromAllTheSynonyms
WHERE [Date] > ''' + #dateFrom + '''
) AS tr
OUTER APPLY (SELECT TOP (1) *
FROM [dbo].[synonym' + #synonymName + '] as syn
WHERE [syn].[Id] = tr.[Id]
ORDER BY [syn].[System.ChangedDate] DESC) result'
INSERT INTO #tmp
EXEC(#sql)
SET #i = #i + 1
END