I am trying to use a SQL Server procedure to create two tables with different names depending on whether a variable is yes (1) or no (0). Everything I've seen refers to temporary tables. I would like to store the table permanently in the database.
CREATE PROCEDURE DUALTYPE
#TableName nvarchar(18), #YesNo int
AS
BEGIN
SELECT *
INTO dbo.[#TableName]
FROM
(SELECT * FROM mytable
WHERE dual = #YesNo) dualtab
END
EXEC DUALTYPE #TableName = 'dualtable' #YesNo = 0
The procedure executes, but it thinks my table name is #TableName. How do I tell it to use 'dualtable' instead?
The code below, using T-SQL, allows for different variable inputs using the same query. The output is then stored in different tables on the database. It is good for making unique samples based on different variable criteria.
The declare statements establish the variable type inputs. Those inputs are then defined using the set statements. 'Set #query' is a string which contains the relevant query (in this case, creating a table based on whether 'dual' is yes vs. no). Because the alterable parameters are contained in a string, they are defined using set #parm N'#...', and then connected to the actual variables in 'EXEC...#parms...'. The query below will produce a table named 'Yes' or 'No' if the YesNo variable is set to 1 or 0, respectively.
declare #TableName sysname;
declare #YesNo int;
declare #parms nvarchar(max);
declare #query nvarchar(max);
set #TableName = 'Yes'
set #YesNo = 1
set #query = '
Select * into [mydb].dbo.' + QUOTENAME(#TableName) + '
from
mytab
where dual = #lvl'
set #parms = N'#lvl int'
EXEC sp_executesql #query, #parms, #lvl = #YesNo
Related
I'm trying to pass user-inputted data to a SQL 'sp_executesql' (Dynamic SQL) statement in order to build a string for the 'SELECT','FROM', and 'WHERE' statements.
I know that SQL Server won't accept a table name or a column name as a parameter. However I was wondering if it was possible to take user-inputted values, store them in a locaL-SQL variable and then use the local variable in the 'FROM' clause?
I know this code would work:
set #tableName = 'SalesData'
set #monthNo = 2
set #sql = N'
select SalesPerson
from ' + #tableName + '
where mon = #monthNo'
exec sp_executesql #sql, N'#monthNo int', #monthNo
But, would this code run?
set #tableName = #ValueTypedByUser
set #monthNo = 2
set #sql = N'
select SalesPerson
from ' + #tableName + '
where mon = #monthNo'
exec sp_executesql #sql, N'#monthNo int', #monthNo
How many tables are you possibly dealing with? If it's a small number you have two better choices:
Use multiple stored procedures instead of one, and call them based on the table you need. You can use a parameter in your calling routine to indicate which SP you want.
Use a parameter to specify the table you want, but then, instead of using a variable to change the table name in your SP, use the following conditionals:
IF #table = 'SalesPersonTable'
BEGIN
SELECT SalesPerson
FROM SalesPersonTable
WHERE mon = #monthNo
END
IF #table = 'OtherTable'
BEGIN
SELECT SalesPerson
FROM OtherTable
WHERE mon = #monthNo
END
This avoids the SQL injection issues, but again, only works if the number of tables is "small" (with "small" being what you want it to be!)
I'm trying to make a bit of SQL script that will output all the table names in a large database, along with the numbers of fields and records in each, and a list of the field names. This will allow us to focus on the tables with data, and look for field names that match from different tables, which might be appropriate places for joins.
To do this, I'm trying to write dynamic SQL that can cycle through all the tables. But I haven't been able to get sp_executesql to yield outputs that I can insert into my table variable. Here's the code I've written so far:
USE MITAS_TEST;
DECLARE #TablesAbstract TABLE(
TableName VARCHAR(50),
NumberOfFields INT,
NumberOfRecords INT
);
DECLARE #NumberOfRowsCounted INTEGER;
SET #NumberOfRowsCounted = 0;
DECLARE #RecSql NVARCHAR(500);
SET #RecSql = 'EXECUTE(''SELECT #NumberOfRows = COUNT(*) FROM ''+#TableName)';
DECLARE #ParmDefinition NVARCHAR(100);
SET #ParmDefinition = '#TableName NVARCHAR(100), #NumberOfRows INTEGER OUTPUT';
DECLARE #TableN NVARCHAR(100);
SET #TableN = 'MITAS_TEST.dbo.AP500';
EXECUTE sp_executesql #RecSql,
#ParmDefinition,
#TableName = #TableN,
#NumberOfRows = #NumberOfRowsCounted OUTPUT;
I get the following error:
Msg 137, Level 15, State 1, Line 1
Must declare the scalar variable "#NumberOfRows"
I would have thought it sufficed to declare #NumberOfRows in the #ParmDefinition field (based on this source: https://technet.microsoft.com/en-us/library/ms188001(v=sql.90).aspx). What am I doing wrong? Is there a better way?
The inner execute looks like:
EXECUTE('SELECT #NumberOfRows = COUNT(*) FROM MITA_TEST.dbo.AP500')
In that context, #NumberOfRows does not exist. You could change that to another call to sp_executesql, passing the output parameter #NumberOfRows down another level.
Assuming that this represents learning code, and not production. Depending on the source of #TableN, this could be susciptable to SQL injection attacks. See quotename in books online.
I am trying to code a stored procedure in SQL that does the following
Takes 2 inputs (BatchType and "Column Name").
Searches database and gives the batchdate and the data in the column = "Column name"
Code is as give below
ALTER PROCEDURE [dbo].[chartmilldata]
-- Add the parameters for the stored procedure here
(#BatchType nvarchar (50),
#Data nvarchar(50))
AS
BEGIN
-- Insert statements for procedure here
SELECT BatchDate,#Data FROM --Database-- WHERE BatchType = #BatchType
END
I am trying to select column from the database based on operator input. But I am not getting the output. It would be great if someone can give me a direction.
You may want to build out your SELECT statement as a string then execute it using sp_executesql.
See this page for more info:
https://msdn.microsoft.com/en-us/library/ms188001.aspx
This will allow you to set your query to substitute in your column name via your variable and then execute the statement. Be sure to sanitize your inputs though!
You'd need to use dynamic SQL, HOWEVER I would not recommend this solution, I don't think there is anything I can add as to why I wouldn't recommend it that isn't explained better in Erland Sommarskog in The Curse and Blessings of Dynamic SQL.
Nonetheless, if you had to do it in a stored procedure you could use something like:
ALTER PROCEDURE [dbo].[chartmilldata]
-- Add the parameters for the stored procedure here
(#BatchType nvarchar (50),
#Data nvarchar(50))
AS
BEGIN
-- DECLARE AND SET SQL TO EXECUTE
DECLARE #SQL NVARCHAR(MAX) = N'SELECT BatchDate = NULL, ' +
QUOTENAME(#Data) + N' = NULL;';
-- CHECK COLUMN IS VALID IN THE TABLE
IF EXISTS
( SELECT 1
FROM sys.columns
WHERE name = #Data
AND object_id = OBJECT_ID('dbo.YourTable', 'U')
)
BEGIN
SET #SQL = 'SELECT BatchDate, ' + QUOTENAME(#Data) +
' FROM dbo.YourTable WHERE BatchType = #BatchType;';
END
EXECUTE sp_executesql #SQL, N'#BatchType NVARCHAR(50)', #BatchType;
END
It would probably be advisable to change your input parameter #Data to be NVARCHAR(128) (or the alias SYSNAME) though, since this is the maximum for column names.
SSMS: 2008 R2
We are having our software system updated, which may contain an unknown number of undocumented changes to the way data is entered and stored in our database. We have asked for documentation, but only have schema compares for "physical" changes to the database, not the way the data is treated. They may change in the future, but for now we have to assume not.
In order to check that our stored procedures work as expected after the update, we would like to run a sample of procedures using a sample of parameters before and after the update to compare the actual data results. The stored procedures here all take a single Id as the parameter (they are used to make SSRS reports within the software system)
I have set some things up, but I am having problems with my approach and would welcome any suggestions about either a better way to do things, or how to fix my approach. The problem is that an error is returned whenever a called stored procedure uses a temporary table. Here is what I have done:
Made a script to get a random sample of Ids for paramaters (only one table used at the moment - that's fine).
ALTER PROC [dbo].[UpdateValidation_GET_RandomIdSample](#TestSizePercent DECIMAL(6,3))
AS
-- This table is already created and will persist both sides of the update
--CREATE TABLE Live_Companion.dbo.UpdateValidationIds
--( Id INT IDENTITY(1,1)NOT NULL PRIMARY KEY CLUSTERED
-- ,MyTableId NT NULL)
IF #TestSizePercent > 100 RAISERROR('Do you even percent, bro?',16,1)
DECLARE #SQL VARCHAR(255)
TRUNCATE TABLE UpdateValidationIds
SET #SQL =
'INSERT dbo.UpdateValidationIds(Id)
SELECT TOP ' + CONVERT(VARCHAR(10),#TestSizePercent) + ' PERCENT ID FROM Live.dbo.MyTable ORDER BY NEWID()'
EXEC (#SQL)
Made a second script to run a stored procedure for each Id in the table:
ALTER PROC [dbo].[UpdateValidation_GET_ProcedureResultsManyTimes](#Procedure_Name VARCHAR(255))
AS
--DECLARE #Procedure_Name VARCHAR(255) = 'Live_Companion.dbo.MyProc'
DECLARE #ID INT
DECLARE #GET_ID CURSOR
DECLARE #SQL VARCHAR(MAX) = ''
DECLARE #MyTableId INT
DECLARE #FirstRun BIT = 1
SET #GET_ID = CURSOR FOR
SELECT Id FROM Live_Companion.dbo.UpdateValidationIds
WHERE MyTableId IS NOT NULL
OPEN #GET_ID
FETCH NEXT FROM #GET_ID INTO #ID
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #MyTableId = MyTableId FROM Live_Companion.dbo.UpdateValidationIds
WHERE Id = #ID
IF #FirstRun = 1
BEGIN
SET #SQL = 'SELECT * INTO #ProcedureOutput FROM OPENROWSET(''SQLNCLI'',''Server=SQL1;Trusted_Connection=yes;'',''EXEC ' + #Procedure_Name + ' ' + CONVERT(VARCHAR(50),#MyTableId) + ''');'
SET #FirstRun = 0
END
ELSE
BEGIN
SET #SQL = #SQL + '
INSERT #ProcedureOutput SELECT * FROM OPENROWSET(''SQLNCLI'',''Server=SQL1;Trusted_Connection=yes;'',''EXEC ' + #Procedure_Name + ' ' + CONVERT(VARCHAR(50),#MyTableId) + ''');'
END
FETCH NEXT FROM #GET_ID INTO #ID
END
SET #SQL = #SQL + '
SELECT * FROM #ProcedureOutput
DROP TABLE #ProcedureOutput'
EXEC (#SQL)
CLOSE #GET_ID
DEALLOCATE #GET_ID
So now I should be able to execute the second procedure for various stored procedures and output the results to file over a range of Ids, then repeat using the saved (initially random) Ids again after the update and compare the results.
The trouble is, it fails when any of the called procedures use a temporary table:
EDIT:
Error Message returned:
Cannot process the object "EXEC Live_Companion.dbo.MyProc 12345". The
OLE DB provider "SQLNCLI10" for linked server "(null)" indicates that
either the object has no columns or the current user does not have
permissions on that object.
Any suggestions or ideas for how to proceed?
Due to the constraints within the workplace I have to use a local stored procedure to call another remote stored proc on a linked sql server, however the problem lies in passing a necessary parameter to the remote stored proc.
This is the query I constructed:
select *
from OPENQUERY([REMOTE_SRVR],'exec db.dbo.dwStoredProc_sp ''#id''')
In order to pass #id to the remote stored proc I understand I could concatenate the above as a string and then use exec
Something along the lines of:
set #query = 'select * from OPENQUERY([REMOTE_SRVR], ''EXEC db.dbo.dwStoredProc_sp '' #id '''''
exec(#query)
I cannot get the local stored proc to successfully call the other. The single quote mess doesn't help!
I get the error: Could not find stored procedure 's'
To help with the quote mess I like to do this in steps. It is more code but easier to understand. I am not sure from your example if #id is an integer. In that case you can lose the double quotes around __ID__.
set #query = 'EXEC db.dbo.dwStoredProc_sp ''__ID__'''
set #query = REPLACE(#query,'__ID__',#id)
set #query = REPLACE(#query,'''','''''')
set #query = REPLACE('SELECT * FROM OPENQUERY([REMOTE_SRVR], ''__REMOTEQUERY__'')','__REMOTEQUERY__',#query)
You could avoid dynamic queries by simply by using EXEC (..., ParamValue) AT LinkedServer (see product's documentation, example [L. Using a parameter with EXECUTE and AT linked_server_name]):
1) On target server:
CREATE PROCEDURE dbo.Proc1( #id NVARCHAR(50) )
AS
SELECT #id AS [id];
GO
2) On the source server you create the linked server and then you can call the stored procedure using EXEC ... AT ... syntax:
DECLARE #p1 NVARCHAR(50);
SET #p1 = N'DROP TABLE dbo.CocoJambo'
EXECUTE (N'dbo.Proc1 ? ' , #p1 ) AT LOCALINKEDSEREV
Output:
id
------------------------
DROP TABLE dbo.CocoJambo