I know this sp returns Orphanded users :
EXEC sp_change_users_login #Action='Report'
I try to find Orphaned users in all databases on SQL Server but it's not returns true result.
DECLARE #name NVARCHAR(MAX),#sql NVARCHAR(MAX), #sql2 NVARCHAR(MAX);
DECLARE #dbname NVARCHAR(MAX);
DECLARE Crs CURSOR
FOR
SELECT name FROM sys.sysdatabases where dbid>4 and name not in(
SELECT DB_NAME(dr_state.database_id) as name
FROM (( sys.availability_groups AS ag JOIN sys.availability_replicas AS ar ON ag.group_id = ar.group_id )
JOIN sys.dm_hadr_availability_replica_states AS ar_state ON ar.replica_id = ar_state.replica_id)
JOIN sys.dm_hadr_database_replica_states dr_state on ag.group_id = dr_state.group_id
and dr_state.replica_id = ar_state.replica_id
where ar_state.role_desc='SECONDARY' AND ar_state.is_local=1
)
OPEN Crs
FETCH NEXT FROM Crs INTO #Name
/*WHILE ## FETCH_STATUS = 0 It means keep returning the cursor by moving to the next record until there are no records left to circulate in the cursor.*/
WHILE ##FETCH_STATUS =0
BEGIN
SELECT name FROM sys.sysdatabases where name=#name
select #dbname=name from sysdatabases where name=#name
use #dbname
EXEC sp_change_users_login #Action='Report'
FETCH NEXT FROM Crs INTO #Name
END
/*CLOSE ve DEALLOCATE commad closed Cursor*/
CLOSE Crs
DEALLOCATE Crs
my error:Msg 102, Level 15, State 1, Line 49
Incorrect syntax near '#dbname'.
If I assign the database name to the #dbname variable, the problem will be solved.
Thanks.
It seems, to me, that you just want to list all the Users, in your databases, that don't have a related LOGIN, excluding Loginless objects. As I mentioned sp_change_users_login is deprecated, so you shouldn't be using that, but you can get the information from the sys.database_principles and sys.server_principles objects. So for a single database you could do something like this:
SELECT db_name(),
dp.[name] AS UserName,
dp.[sid] AS UserSID
FROM sys.database_principals dp
WHERE dp.[type] IN ('U','S')
AND NOT EXISTS (SELECT 1
FROM sys.server_principals sp
WHERE sp.sid = dp.sid)
AND authentication_type > 0;
I'm going to (shamelessly) us my sp_foreachdatabase proc (A CURSOR free version of sp_msforeachdb) and then use the dynamic SQL to put all the data into a single temporary table, and SELECT that at the end. This gives something like this (I have included definition of my objects at the end of this answer):
DECLARE #CRLF nchar(2) = NCHAR(13) + NCHAR(10);
DECLARE #PreCommand nvarchar(MAX) = N'CREATE TABLE #Orphans (DBName sysname,' + #CRLF +
N' UserName sysname,' + #CRLF +
N' UserSID varbinary(85));';
DECLARE #Command nvarchar(MAX) = N'INSERT INTO #Orphans (DBName, UserName, UserSID)' + #CRLF +
N'SELECT db_name(),' + #CRLF +
N' dp.[name] AS UserName,' + #CRLF +
N' dp.[sid] AS UserSID' + #CRLF +
N'FROM sys.database_principals dp' + #CRLF +
N'WHERE dp.[type] IN (''U'',''S'')' + #CRLF +
N' AND NOT EXISTS (SELECT 1' + #CRLF +
N' FROM sys.server_principals sp' + #CRLF +
N' WHERE sp.sid = dp.sid)' + #CRLF +
N' AND authentication_type > 0;';
DECLARE #PostCommand nvarchar(MAX) = N'SELECT DBName, UserName, UserSID FROM #Orphans; DROP TABLE #Orphans;';
DECLARE #CommandRun nvarchar(MAX);
EXEC master.dbo.sp_foreachdatabase #Pre_Command = #PreCommand,
#Command = #Command,
#Post_Command = #PostCommand,
#Exit_On_Error = 0,
#Skip_System = 1,
#Auto_Use = 1,
#Command_Run = #CommandRun OUTPUT;
This puts all the orphaned users into temporary table, with their database's name, and then SELECTs them at the end.
Object definitions:
USE master;
GO
IF NOT EXISTS (SELECT 1 FROM sys.types WHERE [name] = N'objectlist')
CREATE TYPE dbo.objectlist AS table ([name] sysname);
GO
USE master;
GO
CREATE OR ALTER PROC sp_foreachdatabase #Command nvarchar(MAX),
#Delimit_Character nchar(1) = N'?', --Character to be replaced with a delimit identified version of the datbaase name I.e. [master]
#Quote_Character nchar(1) = N'&', --Character to be replaced with a single quoted (') version of the datbaase name I.e. 'master'
#Skip_System bit = 0, --Omits master, msdb, tempdb and model. Ignored if #Database_List has data.
#Skip_User bit = 0, --Omits all user databases. Ignored if #Database_List has data.
#Database_List dbo.objectlist READONLY, --If #Skip_System and #Skip_User equal 1, and this is empty, an error will be thrown
#Auto_Use bit = 0, --Automatically starts each command agaisnt a database with a USE
#Exit_On_Error bit = 1, --If an error is occurs against a single database, the command will still be run against the remainder. Otherwise everything is rolled back
--This does not effect the #Pre_Command and #Post_Command statements
#Pre_Command nvarchar(MAX) = NULL, --Command to run before #Command. Does not use Character Replacements. Run against master DB.
#Post_Command nvarchar(MAX) = NULL, --Command to run after #Command. Does not use Character Replacements. Run against master DB.
#Command_Run nvarchar(MAX) = NULL OUTPUT --Returns the generated and replaced command, for trouble shooting
AS BEGIN
--Do some checking of passed values first
--Check that #Skip_System, #Skip_User aren't both 0 or that #Database_List has some rows
IF (#Skip_System = 1 AND #Skip_User = 1 AND NOT EXISTS (SELECT 1 FROM #Database_List))
THROW 62401, N'System and User databases cannot be skipped if a Database List is not supplied.', 16;
IF #Delimit_Character IS NULL OR #Delimit_Character = ''
THROW 62402, N'#Delimit_Character cannot have a value of NULL or ''''.', 16;
IF #Quote_Character IS NULL OR #Quote_Character = ''
THROW 62403, N'#Quote_Character cannot have a value of NULL or ''''.', 16;
IF #Skip_User IS NULL
THROW 62404, N'#Skip_User cannot have a value of NULL.', 16;
IF #Skip_System IS NULL
THROW 62405, N'#Skip_System cannot have a value of NULL.', 16;
IF #Auto_Use IS NULL
PRINT N'#Auto_Use has a value of NULL. Behaviour will be as if the value is 0.';
DECLARE #CRLF nchar(2) = NCHAR(13) + NCHAR(10);
DECLARE #RC int;
--Add the Pre Command to the batch
SET #Command_Run = ISNULL(N'/* --- Pre Command Begin. --- */' + #CRLF + #CRLF + N'USE master;' + #CRLF + #CRLF + #Pre_Command + #CRLF + #CRLF + N'/* --- Pre Command End. --- */', N'');
--Get the databases we need to deal with
--As #Database_List might be empty and it's READONLY, and we're going to do the command in database_id order we need another variable.
DECLARE #DBs table (database_id int,
database_name sysname);
IF EXISTS (SELECT 1 FROM #Database_List)
INSERT INTO #DBs (database_id,database_name)
SELECT d.database_id,
d.[name]
FROM sys.databases d
JOIN #Database_List DL ON d.[name] = DL.[name];
ELSE
INSERT INTO #DBs (database_id,database_name)
SELECT d.database_id,
d.[name]
FROM sys.databases d
WHERE (d.database_id <= 4 AND #Skip_System = 0) OR (d.database_id > 4 AND #Skip_User = 0);
SET #Command_Run = #Command_Run + #CRLF + #CRLF +
N'/* --- Begin command for each database. --- */' + #CRLF + #CRLF +
CASE WHEN #Exit_On_Error = 0 THEN N'--Turning XACT_ABORT off due to #Exit_On_Error parameter' + #CRLF + #CRLF + N'SET XACT_ABORT OFF;' + #CRLF + N'DECLARE #Error nvarchar(4000);' ELSE N'SET XACT_ABORT ON;' END +
(SELECT #CRLF + #CRLF +
N'/* --- Running #Command against database ' + QUOTENAME(DB.database_name,'''') + N'. --- */' + #CRLF + #CRLF +
CASE WHEN #Auto_Use = 1 THEN N'USE ' + QUOTENAME(DB.database_name) + N';' + #CRLF + #CRLF ELSE N'' END +
N'BEGIN TRY' + #CRLF + #CRLF +
REPLACE(REPLACE(#Command, #Delimit_Character, QUOTENAME(DB.database_name)),#Quote_Character, 'N' + QUOTENAME(DB.database_name,'''')) + #CRLF + #CRLF +
'END TRY' + #CRLF +
N'BEGIN CATCH' + #CRLF +
CASE WHEN #Exit_On_Error = 0 THEN N' SET #Error = N''The following error occured during the batch, but has been skipped:'' + NCHAR(13) + NCHAR(10) + ' + #CRLF +
N' N''Msg '' + CONVERT(nvarchar(6),ERROR_NUMBER()) + '', Level '' + CONVERT(nvarchar(6),ERROR_SEVERITY()) + '', State '' + CONVERT(nvarchar(6),ERROR_STATE()) + '', Line '' + CONVERT(nvarchar(6),ERROR_LINE()) + NCHAR(13) + NCHAR(10) +' + #CRLF +
N' ERROR_MESSAGE();' + #CRLF +
N' PRINT #Error;' + #CRLF +
N' SET #RC = ERROR_NUMBER();'
ELSE N' THROW;'
END + #CRLF +
N'END CATCH;' + #CRLF +
N'/* --- Completed #Command against database ' + QUOTENAME(DB.database_name,'''') + N'. --- */'
FROM #DBs DB
FOR XML PATH(N''),TYPE).value('.','nvarchar(MAX)') + #CRLF + #CRLF +
CASE WHEN #Exit_On_Error = 0 THEN N'--Turning XACT_ABORT back on due to #Exit_On_Error parameter' + #CRLF + #CRLF + N'SET XACT_ABORT ON;' ELSE N'' END;
SET #Command_Run = #Command_Run + ISNULL(#CRLF + #CRLF + N'/* --- Post Command Begin. --- */' + #CRLF + #CRLF + N'USE master;' + #CRLF + #CRLF + #Post_Command + #CRLF + #CRLF + N'/* --- Post Command End. --- */', N'');
EXEC sp_executesql #Command_Run, N'#RC int OUTPUT', #RC = #RC;
SET #RC = ISNULL(#RC, 0);
RETURN #RC;
END;
GO
Related
I have created the below dynamic query to check if length of particular attribute is more than 50 or not. I am trying to make the length size also dynamic but getting below error.
Msg 207, Level 16, State 1, Line 7
Invalid column name '50'.
The query I came up with is as below. I am pretty new to SQL and trying to resolve this error. Thanks!
--Rule A.5.Summary :- Length
/* The 'DECLARE' statements below are used to
define standard test parameters and their datatypes. The values for these
parameters will be updated for each of the DQ dimensions/KDEs being tested */
DECLARE #DQ_DIMENSION_RULE VARCHAR(100)
DECLARE #RULE_NO VARCHAR(100)
DECLARE #TABLE_NAME VARCHAR(100)
DECLARE #DATA_ATTRIBUTE VARCHAR(100)
DECLARE #LENGTH_SIZE INT
/*The 'SET' statements below are used to
assign values to each of the test parameters declared above.
The values will depend on the DQ dimension/KDE being tested.*/
SET #DQ_DIMENSION_RULE = 'Accuracy - Negative Values'
SET #RULE_NO = 'A.4'
SET #TABLE_NAME = 'TRANSACTIONS'
SET #DATA_ATTRIBUTE = 'TRANSACTIONID'
SET #LENGTH_SIZE = 50
DECLARE #sql nvarchar(max) = N'
SELECT
' + QUOTENAME(#DQ_DIMENSION_RULE, '''') + N' AS [DQ_Dimension_Rule],
' + QUOTENAME(#RULE_NO, '''') + N' AS [Rule_No],
' + QUOTENAME(#TABLE_NAME, '''') + N' AS [Table Name],
' + QUOTENAME(#DATA_ATTRIBUTE, '''') + N' AS [Column Name],
case when SUM(CASE WHEN LEN(' + QUOTENAME(#DATA_ATTRIBUTE) + N') >' + QUOTENAME(#LENGTH_SIZE) + N' THEN 1 ELSE 0 END) > 0 then ''Y'' else ''N'' end as [Potential Issue(Y/N)]
FROM ' + QUOTENAME(#TABLE_NAME)
-- The data from 'SELECT' statement is being inserted into the summary table
--INSERT INTO summary
EXEC sp_executesql #sql;
You should not put QUOTENAME() around the integer values, the integers are not table names that you need to quote, etc.
You should just convert the integers to strings, without quotes...
DECLARE #sql nvarchar(max) = N'
SELECT
' + QUOTENAME(#DQ_DIMENSION_RULE, '''') + N' AS [DQ_Dimension_Rule],
' + QUOTENAME(#RULE_NO, '''') + N' AS [Rule_No],
' + QUOTENAME(#TABLE_NAME, '''') + N' AS [Table Name],
' + QUOTENAME(#DATA_ATTRIBUTE, '''') + N' AS [Column Name],
case when SUM(CASE WHEN LEN(' + QUOTENAME(#DATA_ATTRIBUTE) + N') >' + CAST(#LENGTHSIZE AS NVARCHAR(MAX)) + N' THEN 1 ELSE 0 END) > 0 then ''Y'' else ''N'' end as [Potential Issue(Y/N)]
FROM ' + QUOTENAME(#TABLE_NAME)
QUOTENAME(#LENGTHSIZE) => CAST(#LENGTHSIZE AS NVARCHAR(MAX))
Better than injecting it, parametrise it:
DECLARE #DQ_DIMENSION_RULE VARCHAR(100);
DECLARE #RULE_NO VARCHAR(100);
DECLARE #TABLE_NAME sysname; --Correct data type for object names
DECLARE #DATA_ATTRIBUTE VARCHAR(100);
DECLARE #LENGTH_SIZE INT;
SET #DQ_DIMENSION_RULE = 'Accuracy - Negative Values';
SET #RULE_NO = 'A.4';
SET #TABLE_NAME = 'TRANSACTIONS';
SET #DATA_ATTRIBUTE = 'TRANSACTIONID';
SET #LENGTH_SIZE = 50;
DECLARE #sql nvarchar(max) = N'
SELECT
' + QUOTENAME(#DQ_DIMENSION_RULE, '''') + N' AS [DQ_Dimension_Rule],
' + QUOTENAME(#RULE_NO, '''') + N' AS [Rule_No],
N' + QUOTENAME(#TABLE_NAME, '''') + N' AS [Table Name],
' + QUOTENAME(#DATA_ATTRIBUTE, '''') + N' AS [Column Name],
case when SUM(CASE WHEN LEN(' + QUOTENAME(#DATA_ATTRIBUTE) + N') > #LENGTH_SIZE THEN 1 ELSE 0 END) > 0 then ''Y'' else ''N'' end as [Potential Issue(Y/N)]
FROM ' + QUOTENAME(#TABLE_NAME) + N';';
EXEC sys.sp_executesql #sql, N'#LENGTH_SIZE int', #LENGTH_SIZE;
I've also changed the data type of your dynamic object and ensured that it the literal string is injected with a nvarchar notation character too.
I am trying to make the procedure where I can get the data from MS SQL. Here I am getting the error
Msg 245, Level 16, State 1, Procedure GET_FUNCTIONS, Line 15 [Batch Start Line 33]
Conversion failed when converting the varchar value ' and f.FUNCTION_ID in '' to data type int.
CREATE procedure [dbo].[GET_FUNCTIONS]
#Function_Id int,
#EntityId int,
#OrderBy varchar(10)
as
begin
declare #Sql nvarchar(max)
if(#Function_Id=0 AND #EntityId=0)
set #Sql = 'select
f.Function_Code,f.FUNCTION_ID,f.EntityId,be.ENTITY_SHORT_NAME,f.FUNCTION_NAME,
f.FUNCTION_DESC,f.CREATED_DATE,f.MODIFIED_DATE from FUNCTIONS f
inner join BusinessEntity be on f.EntityId=be.ID
WHERE f.ACTIVE=1 '
if(#Function_Id!=0)
set #Sql += ' and f.FUNCTION_ID in ''' + #Function_Id + ''''
if(#EntityId!=0)
set #Sql += ' and f.EntityId = cast('+convert(varchar(12) ,#EntityId)+') '
set #Sql += ' order by FUNCTION_ID '+ #OrderBy
print #Sql
end
GO
Thanks everyone for your answers. I have got the answer by my own.
I have removed if(#Function_Id=0 AND #EntityId=0) and changed the data type of #Function_Id and #EntityId to varchar(100), so solved all errors.
I'm, making a few guesses here, which I add some comments about, however, this should be what you are after. I firstly ensure that I parametrise the values, but also validate and quote the value of #OrderBy:
CREATE PROCEDURE [dbo].[GET_FUNCTIONS] #Function_Id int =NULL, --Instead of passing 0as "select all" use NULL
#EntityId int = NULL, --Instead of passing 0as "select all" use NULL
#OrderBy nvarchar(4) --guessing this is mean to be ASC or DESC?
AS
BEGIN
IF #OrderBy NOT IN (N'ASC','DESC')
--Error is not ASC or DESC
THROW 51473, N'#OrderBy can only have a value of ''ASC'' or ''DESC''.',16; --51473 was a random error number; you can use one that is appropriate for your environment.
DECLARE #SQL nvarchar(MAX),
#CRLF nchar(2) = NCHAR(13) + NCHAR(10);
SET #SQL = N'SELECT f.Function_Code,' + + #CRLF +
N' f.FUNCTION_ID,' + + #CRLF +
N' f.EntityId,' + + #CRLF +
N' be.ENTITY_SHORT_NAME,' + + #CRLF +
N' f.FUNCTION_NAME,' + + #CRLF +
N' f.FUNCTION_DESC,' + + #CRLF +
N' f.CREATED_DATE,' + + #CRLF +
N' f.MODIFIED_DATE' + + #CRLF +
N'FROM FUNCTIONS f' + + #CRLF +
N' INNER JOIN BusinessEntity be ON f.EntityId = be.ID' + + #CRLF +
N'WHERE f.ACTIVE = 1';
IF (#Function_Id IS NOT NULL)
SET #SQL = #SQL + #CRLF + N' AND f.FUNCTION_ID = #Function_Id'
IF (#EntityId IS NOT NULL)
SET #SQL = #SQL + #CRLF + N' AND f.EntityId = #EntityId'
SET #SQL = #SQL + #CRLF +
N'ORDER BY F.FUNCTION_ID ' + QUOTENAME(#OrderBy) + N';'
--PRINT #SQL; --Uncomment for debugging.
EXEC sp_executesql #SQL, N'#Function_Id int, #EntityId int', #Function_Id, #EntityId;
END;
GO
I am trying to create a dynamic SQL query and perform insert operation. But for some reason, I am getting
error: Conversion failed when converting the varchar value '
Please can anyone help me in resolving this error. Getting error at values('#file_type_id'
Below is my SQL code:
DECLARE #file_name VARCHAR(100) = 'MyFile'
DECLARE #file_type_id INT = 1
DEclare #filing_id bigint = 57
DECLARE #created_at DATETIME = GETDATE()
DECLARE #created_by BIGINT = 2
DECLARE #is_confidential bit = 1
set #insertquery =
'
DECLARE #Document AS VARBINARY(MAX)
SELECT #Document = CAST(bulkcolumn AS VARBINARY(MAX)) FROM OPENROWSET( BULK ''C:\SampleTestFiles\MyWordDoc.doc'', SINGLE_BLOB ) AS Doc
Insert ['+#databasename+'].['+#schemaname+'].['+#tablename+']
( [file_type_id], [file], [file_name], [filing_id], [created_at], [created_by], [is_confidential])
values ( '#file_type_id', #Document, #file_name, #filing_id , #created_at, #created_by, #is_confidential)
'
exec (#insertquery)
I suspect this is what you are after. Note that I have parametrised the variables, and safely quoted the values of your dynamic objects:
DECLARE #databasename sysname,
#schemaname sysname,
#tablename sysname;
DECLARE #file_name varchar(100) = 'MyFile',
#file_type_id int = 1,
#filing_id bigint = 57,
#created_at datetime = GETDATE(),
#created_by bigint = 2,
#is_confidential bit = 1,
#CRLF nchar(2) = NCHAR(13) + NCHAR(10),
#insertquery nvarchar(MAX);
SET #insertquery = N'DECLARE #Document AS VARBINARY(MAX);' + #CRLF +
N'SELECT #Document = CAST(bulkcolumn AS VARBINARY(MAX)) FROM OPENROWSET( BULK ''C:\SampleTestFiles\MyWordDoc.doc'', SINGLE_BLOB ) AS Doc;' + #CRLF + #CRLF +
N'INSERT INTO ' + QUOTENAME(#databasename) + '.' + QUOTENAME(#schemaname) + N'.' + QUOTENAME(#tablename) + N' ( [file_type_id], [file], [file_name], [filing_id], [created_at], [created_by], [is_confidential])' + #CRLF +
N'VALUES (#file_type_id, #Document, #file_name, #filing_id , #created_at, #created_by, #is_confidential);';
--PRINT #insertquery; --Your best friend.
EXEC sp_executesql #insertquery, N'#file_name varchar(100),#file_type_id int,#filing_id bigint,#created_at datetime, #created_by bigint,#is_confidential bit', #file_name, #file_type_id, #filing_id, #created_at, #created_by, #is_confidential;
I can't test the above, however, if you get any errors I would suggest looking at your best friend. if, however, I use the declarations below...:
DECLARE #databasename sysname = N'MyDB',
#schemaname sysname = N'dbo',
#tablename sysname = N'MyTable';
I get this statement, which appears to be correct:
DECLARE #Document AS VARBINARY(MAX);
SELECT #Document = CAST(bulkcolumn AS VARBINARY(MAX)) FROM OPENROWSET( BULK 'C:\SampleTestFiles\MyWordDoc.doc', SINGLE_BLOB ) AS Doc;
INSERT INTO [MyDB].[dbo].[MyTable] ( [file_type_id], [file], [file_name], [filing_id], [created_at], [created_by], [is_confidential])
VALUES (#file_type_id, #Document, #file_name, #filing_id , #created_at, #created_by, #is_confidential);
Edit: to address the comment from the OP "How do I put another variable for file path." the correct syntax of that would be to make use of the second parameter from QUOTENAME (note that this strongly assumes that #filepath can never have a length longer than 128 characters):
SET #insertquery = N'DECLARE #Document AS VARBINARY(MAX);' + #CRLF +
N'SELECT #Document = CAST(bulkcolumn AS VARBINARY(MAX)) FROM OPENROWSET( BULK ' + QUOTENAME(#filepath,'''') + N', SINGLE_BLOB ) AS Doc;' + #CRLF + #CRLF +
N'INSERT INTO ' + QUOTENAME(#databasename) + '.' + QUOTENAME(#schemaname) + N'.' + QUOTENAME(#tablename) + N' ( [file_type_id], [file], [file_name], [filing_id], [created_at], [created_by], [is_confidential])' + #CRLF +
N'VALUES (#file_type_id, #Document, #file_name, #filing_id , #created_at, #created_by, #is_confidential);';
If I write the tablename after 'FROM' the query works. If I use the result of a select statement the query does not resolve. Is there a workaround?
I have tried resolving the tablename as an output from a stored procedure but I cannot seem to use exec in a stored procedure or function without receiving an error
ALTER PROCEDURE [dbo].[EDOFA_TABLE]
(
#DTCODE nvarchar(max) = null
,#DTAMPO nvarchar(max) = null
,#DTNDOS float = null
,#DTTFAC nvarchar(max) = null
,#DTCDEB nvarchar(max) = null
,#DTNDEB float = null
,#DTDAPP float = null
)
AS
BEGIN
INSERT INTO EDOFA (DTCODE
,DTAMPO
,DTNDOS
,DTTFAC
,DTCDEB
,DTNDEB
,DTDAPP)
SELECT DTCODE
,DTAMPO
,DTNDOS
,DTTFAC
,DTCDEB
,DTNDEB
,DTDAPP
FROM (SELECT max(name) FROM sys.tables WHERE name like 'EDOFA%')
WHERE DTCODE = #DTCODE
OR DTAMPO = #DTAMPO
OR DTNDOS = #DTNDOS
OR DTTFAC = #DTTFAC
OR DTCDEB = #DTCDEB
OR DTNDEB = #DTNDEB
OR DTDAPP = #DTDAPP
END
Incorrect syntax near keyword where
As I stated in my comment "That isn't how SQL works. You can't replace an object with an expression, variable name, etc. It has to be a ltieral; you need to use parametrised dynamic SQL.". I believe this does what you're after.
ALTER PROCEDURE [dbo].[EDOFA_TABLE] (#DTCODE nvarchar(MAX) = NULL,
#DTAMPO nvarchar(MAX) = NULL,
#DTNDOS float = NULL,
#DTTFAC nvarchar(MAX) = NULL,
#DTCDEB nvarchar(MAX) = NULL,
#DTNDEB float = NULL,
#DTDAPP float = NULL)
AS
BEGIN
DECLARE #SQL nvarchar(MAX);
SET #SQL = N'INSERT INTO dbo.EDOFA (DTCODE,' + NCHAR(13) + NCHAR(10) +
N' DTAMPO,' + NCHAR(13) + NCHAR(10) +
N' DTNDOS,' + NCHAR(13) + NCHAR(10) +
N' DTTFAC,' + NCHAR(13) + NCHAR(10) +
N' DTCDEB,' + NCHAR(13) + NCHAR(10) +
N' DTNDEB,' + NCHAR(13) + NCHAR(10) +
N' DTDAPP)' + NCHAR(13) + NCHAR(10) +
N'SELECT DTCODE,' + NCHAR(13) + NCHAR(10) +
N' DTAMPO,' + NCHAR(13) + NCHAR(10) +
N' DTNDOS,' + NCHAR(13) + NCHAR(10) +
N' DTTFAC,' + NCHAR(13) + NCHAR(10) +
N' DTCDEB,' + NCHAR(13) + NCHAR(10) +
N' DTNDEB,' + NCHAR(13) + NCHAR(10) +
N' DTDAPP' + NCHAR(13) + NCHAR(10) +
N'FROM dbo.' + QUOTENAME((SELECT MAX([name]) FROM sys.tables WHERE name LIKE 'EDOFA%')) + NCHAR(13) + NCHAR(10) +
N'WHERE DTCODE = #DTCODE' + NCHAR(13) + NCHAR(10) +
N' OR DTAMPO = #DTAMPO' + NCHAR(13) + NCHAR(10) +
N' OR DTNDOS = #DTNDOS' + NCHAR(13) + NCHAR(10) +
N' OR DTTFAC = #DTTFAC' + NCHAR(13) + NCHAR(10) +
N' OR DTCDEB = #DTCDEB' + NCHAR(13) + NCHAR(10) +
N' OR DTNDEB = #DTNDEB' + NCHAR(13) + NCHAR(10) +
N' OR DTDAPP = #DTDAPP;';
DECLARE #Params nvarchar(MAX) = N'#DTCODE nvarchar(MAX),' +
N'#DTAMPO nvarchar(MAX),' +
N'#DTNDOS float,' +
N'#DTTFAC nvarchar(MAX),' +
N'#DTCDEB nvarchar(MAX),' +
N'#DTNDEB float,' +
N'#DTDAPP float';
--PRINT #SQL; --Your best friend
EXEC sp_executesql #SQL, #Params, #DTCODE, #DTAMPO, #DTNDOS, #DTTFAC, #DTCDEB, #DTNDEB, #DTDAPP;
END;
I build a dynamic statement, and put that into #SQL; along with ensuring I properly quote the dynamic object name, using QUOTENAME. Then I build the parameters and pass them all to the dynamic statement us sp_executesql.
If you get stuck, uncomment your best friend (and comment out the EXEC) and debug the printed SQL.
Your from clause returns a scalar, and doesn't name it. Also the subquery in the from clause needs an alias. That's causing the error you get.
Once you add an alias, you'll get other errors. e.g. DTAMPO is not a column returned by the FROM clause
You cannot dynamically include identifiers in SQL, but you can include constant values. To do so, use sp_executesql like this:
BEGIN
DECLARE #sql NVARCHAR(MAX);
SET #sql = '
INSERT INTO EDOFA (DTCODE, DTAMPO, DTNDOS, DTTFAC, DTCDEB, DTNDEB, DTDAPP)
SELECT DTCODE, DTAMPO, DTNDOS, DTTFAC, DTCDEB, DTNDEB, DTDAPP
FROM [table]
WHERE DTCODE = #DTCODE OR
DTAMPO = #DTAMPO OR
DTNDOS = #DTNDOS OR
DTTFAC = #DTTFAC OR
DTCDEB = #DTCDEB OR
DTNDEB = #DTNDEB OR
DTDAPP = #DTDAPP';
-- Replace the table name because, alas, we cannot parameterize that
SET #SQL = REPLACE(#SQL,
'[table]',
(SELECT max(name) FROM sys.tables WHERE name like 'EDOFA%')
);
-- Pass everything else as parameters
EXEC sp_executesql #SQL,
N'
#DTCODE nvarchar(max),
#DTAMPO nvarchar(max),
#DTNDOS float,
#DTTFAC nvarchar(max),
#DTCDEB nvarchar(max),
#DTNDEB float,
#DTDAPP float',
#DTCODE=#DTCODE, #DTAMPO=#DTAMPO, #DTNDOS=#DTNDOS, #DTTFAC=#DTTFAC,
#DTCDEB=#DTCDEB, #DTNDEB=#DTNDEB, #DTDAPP=#DTDAPP;
END;
The code may be a bit more cumbersome to write, but using parameters is definitely a best practice when passing values into SQL queries.
Is there is a way to generate a TVP from an existing table? I have tried this, https://dba.stackexchange.com/questions/12596/using-tables-as-table-valued-parameters-tvp but it's showing me syntax error?
-- you would pass these two in as parameters of course:
DECLARE
#TableName SYSNAME = N'LocationTable',
#TypeName SYSNAME = N'LocationTypeTable';
DECLARE #sql NVARCHAR(MAX) = N'';
SELECT #sql = #sql + N',' + CHAR(13) + CHAR(10) + CHAR(9)
+ QUOTENAME(c.name) + ' '
+ s.name + CASE WHEN LOWER(s.name) LIKE '%char' THEN
'(' + CONVERT(VARCHAR(12), (c.max_length/
(CASE LOWER(LEFT(s.name, 1)) WHEN N'n' THEN 2 ELSE 1 END)) + ')'
ELSE '' END
-- need much more conditionals here for other data types
FROM sys.columns AS c
INNER JOIN sys.types AS s
ON c.system_type_id = s.system_type_id
AND c.user_type_id = s.user_type_id
WHERE c.[object_id] = OBJECT_ID(#TableName);
SELECT #sql = N'CREATE TYPE ' + #TypeName
+ ' AS TABLE ' + CHAR(13) + CHAR(10) + (' + STUFF(#sql, 1, 1, '')
+ CHAR(13) + CHAR(10) + ');';
PRINT #sql;
-- EXEC sp_executesql #sql;
I have made modifications to your code above. This code executes but returns no values. What are you looking for exactly.
-- you would pass these two in as parameters of course:
DECLARE
#TableName SYSNAME
Set #TableName = N'LocationTable'
DECLARE
#TypeName SYSNAME
Set #TypeName = N'LocationTypeTable'
DECLARE #sql NVARCHAR(4000)
SET #sql= N'';
SELECT #sql = N',' + CHAR(13) + CHAR(10) + CHAR(9)
+ QUOTENAME(c.name) + ' '
+ s.name + CASE WHEN LOWER(s.name) LIKE '%char' THEN
-- '(' +
CONVERT(VARCHAR(12), (c.max_length/
(CASE LOWER(LEFT(s.name, 1)) WHEN N'n' THEN 2 ELSE 1 END)))
-- + ')'
ELSE '' END
-- need much more conditionals here for other data types
FROM sys.columns AS c
INNER JOIN sys.types AS s
ON c.system_type_id = s.system_type_id
AND c.user_type_id = s.user_type_id
WHERE c.[object_id] = OBJECT_ID(#TableName);
SELECT #sql = N'CREATE TYPE ' + #TypeName
+ ' AS TABLE ' + CHAR(13) + CHAR(10) + '(' + STUFF(#sql, 1, 1, '')
+ CHAR(13) + CHAR(10) + ');';
PRINT #sql;
-- EXEC sp_executesql #sql;