Change all empty strings to NULL using one query with loop. SQL - sql

I have table with columns:
[1959], [1960], [1961] ... [2016];
All these columns are type nvarchar(255), some of these have empty strings ''. I want to change empty strings to NULL. I write query:
DECLARE #empty nvarchar(255);
SET #empty='';
DECLARE #cnt INT = 1959;
WHILE #cnt < 2017
BEGIN
declare #sql nvarchar(1000)
set #sql = 'UPDATE [HDProjEtap1Proba1].[dbo].[TESTGDP] SET [' + CAST(#cnt as nvarchar(255)) + '] = null WHERE ['+ CAST(#cnt as nvarchar(255)) +'] = ' + #empty;
EXEC(#sql);
SET #cnt = #cnt + 1;
END;
But dosn't work. Message error:
Msg 102, Level 15, State 1, Line 1
Incorrect syntax near '='.
Can you tell me how do it right?

#empty is empty. It doesn't represent the empty string when you concatenate it.
One simple solution is:
SET #empty = '''';
However, you can do the same thing by passing a parameter using sp_executesql. And I like that approach:
declare #cnt INT = 1959;
while #cnt < 2017
begin
declare #sql nvarchar(1000)
set #sql = 'UPDATE [HDProjEtap1Proba1].[dbo].[TESTGDP] SET [' + CAST(#cnt as nvarchar(255)) + '] = null WHERE ['+ CAST(#cnt as nvarchar(255)) +'] = #empty';
exec sp_executesql #sql, N'#empty nvarchar(255)', #empty = '';
set #cnt = #cnt + 1;
end;
Or event:
declare #cnt INT = 1959;
while #cnt < 2017
begin
declare #sql nvarchar(1000)
set #sql = '
UPDATE [HDProjEtap1Proba1].[dbo].[TESTGDP]
SET [#colname] = null
WHERE [#colname] = #empty';
set #sql = replace(#sql, '#colname', cast(#cnt as varchar(255));
exec sp_executesql #sql, N'#empty nvarchar(255)', #empty = '';
set #cnt = #cnt + 1;
end;
Notice that in this version, the SQL statement is easy to read (and hence to modify and debug). It is defined with parameters that are then subsequently replaced.

Related

Getting OUTPUT from Nested Dynamic SQL with a Remote Server

I am trying to execute some logic on a remote server using dynamic SQL, but when I set the dynamic SQL and try to call it nothing happens.. If I call the execution without making the execution dynamic it works without issue.
Not sure if what I am trying to do is allowed when passing OUTPUT values but figured I would see if anyone has any thoughts
DECLARE #sqlserver NVARCHAR(MAX) = ''
DECLARE #DBName NVARCHAR(MAX) = ''
DECLARE #parms NVARCHAR(MAX) = N'#output INT OUT', #output INT, #sql NVARCHAR(MAX) = '
;WITH DuplicateStatusCodes AS (
SELECT CodeTypeID FROM EDDSDBO.Code WHERE Name = ''Master'' OR Name = ''Unique'' OR Name = ''Duplicate''
)
SELECT ROW_NUMBER() OVER (ORDER BY CodeTypeID) RN, CodeTypeID INTO #temp
FROM DuplicateStatusCodes
GROUP BY CodeTypeID
HAVING COUNT(CodeTypeID) = 3
DECLARE #count INT = (SELECT COUNT(1) FROM #temp)
DECLARE #ctr INT = 1;
IF #count > 0
BEGIN
WHILE #ctr <= #count BEGIN
DECLARE #parms NVARCHAR(MAX) = N''#inneroutput INT OUT'';
DECLARE #inneroutput INT
DECLARE #codeartifact INT = (SELECT CodeTypeID FROM #temp WHERE RN <= 1 * #ctr and RN > 1 * (#ctr - 1))
DECLARE #duplicatestatuscheck NVARCHAR(MAX) = ''SELECT #inneroutput = COUNT(1) FROM eddsdbo.codeartifact_''+CAST(#codeartifact AS VARCHAR)+''''
EXEC sys.sp_executesql #duplicatestatuscheck, #parms, #inneroutput OUT
SELECT #output = #inneroutput
IF #inneroutput > 0
BREAK;
SET #ctr = #ctr + 1;
END
END
ELSE
BEGIN
SELECT #output = ''0''
END
'
DECLARE #linksql NVARCHAR(MAX) = '
DECLARE #sql NVARCHAR(MAX)
DECLARE #parms NVARCHAR(MAX) = N''#output INT OUT''
EXEC '+QUOTENAME(#sqlserver)+'.'+QUOTENAME(#DBName)+'.sys.sp_executesql #sql, #parms, #output = #output OUT'
--This does not work
EXEC sp_executesql #linksql, #parms, #output = #output
PRINT #output
--This works fine
EXEC [RemoteServerName].[DatabaseName].sys.sp_executesql #sql, #parms, #output = #output OUT ```
Appreciate any help
you would need to use OPENROWSET. Something like this:
DECLARE #ServerName VARCHAR(255) = '...'
,#DatabaseName VARCHAR(255) = '...'
,#Pwd VARCHAR(255) = '...'
,#UID VARCHAR(255) = '...'
,#Name VARCHAR(255) = 'Master'
DECLARE #connect varchar(max) = '''Server=' + #ServerName + ';database=' + #DatabaseName + ';Uid=' + #UID + ';Pwd=' + #Pwd + ';'''
declare #sqlstring varchar(max) = 'SET ANSI_NULLS OFF; SET ANSI_WARNINGS OFF;
SELECT CodeTypeID FROM EDDSDBO.Code WHERE Name = ''''' + #Name + ''''' '
declare #TheExecutableStr varchar(max) = '
SELECT *
FROM OPENROWSET(''SQLNCLI'', ' + #connect + ',
''' + #sqlstring + ''')'
SELECT #TheExecutableStr
--EXEC(#TheExecutableStr)
Please note you have to count single quote very diligently, hence SELECT #TheExecutableStr. It is for debugging purposes. You can view the query to be executed and actually copy/paste and execute before uncommenting last statement.
Hope it helps.
just noticed, you have sys.sp_executesql within sys.sp_executesql which might be problematic. you might need to find a way to refactor the code.

Compare two table using dynamic database

I have 2 table (same name) on 2 different database
I want compare these table, this is my procedure
ALTER PROCEDURE COUNTCOLUMN
#TABLENAME NVARCHAR(MAX),
#DATABASENAME1 NVARCHAR(MAX),
#DATABASENAME2 NVARCHAR(MAX)
AS
BEGIN
DECLARE #COLNAME NVARCHAR(MAX)
DECLARE #ROWCOUNT INT
DECLARE #ROWCOUNT2 INT
DECLARE #NUMB1 INT
DECLARE #NUMB2 INT
DECLARE #SQLQUERY NVARCHAR(MAX)
DECLARE #SQLQUERY2 NVARCHAR(MAX)
DECLARE #SQLQUERY3 NVARCHAR(MAX)
DECLARE #SQLQUERY4 NVARCHAR(MAX)
DECLARE #SQLQUERY5 NVARCHAR(MAX)
DECLARE #SQLQUERY6 NVARCHAR(MAX)
SET #SQLQUERY = 'SELECT #NUMBER = COUNT(*) FROM information_schema.columns WHERE table_name = '+N'#TESTTB'+''
SET #SQLQUERY2 = 'USE ' + #DATABASENAME1
SET #SQLQUERY3 = 'USE ' + #DATABASENAME2
EXECUTE sp_executesql #SQLQUERY2
EXECUTE sp_executesql #SQLQUERY, N'#TESTTB NVARCHAR(MAX), #NUMBER INT OUTPUT',#TESTTB = #TABLENAME ,#NUMBER = #NUMB1 OUTPUT
EXECUTE sp_executesql #SQLQUERY3
EXECUTE sp_executesql #SQLQUERY, N'#TESTTB NVARCHAR(MAX), #NUMBER INT OUTPUT',#TESTTB = #TABLENAME ,#NUMBER = #NUMB2 OUTPUT
IF(#NUMB1 <= #NUMB2)
BEGIN
DECLARE #NUMB INT
SET #NUMB = 1
WHILE (#NUMB <= #NUMB1)
BEGIN
IF (EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'TEMP1'))
BEGIN
DROP TABLE TEMP1
END
IF (EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'TEMP2'))
BEGIN
DROP TABLE TEMP2
END
ELSE
BEGIN
SET #SQLQUERY4 = 'SELECT #COL_NAME = COL_NAME(OBJECT_ID(#TABLENAME), #NUMB)'
EXECUTE sp_executesql #SQLQUERY2
EXECUTE sp_executesql #SQLQUERY4, N'#TABLENAME NVARCHAR(MAX),#NUMB INT, #COL_NAME NVARCHAR(MAX) OUTPUT', #TABLENAME = #TABLENAME,#NUMB=#NUMB, #COL_NAME = #COLNAME OUTPUT
SET #SQLQUERY5 = 'SELECT '+ #COLNAME + ' INTO TEST_DB..TEMP1 FROM ' + #TABLENAME
EXECUTE sp_executesql #SQLQUERY2
EXECUTE sp_executesql #SQLQUERY5
EXECUTE sp_executesql #SQLQUERY3
EXECUTE sp_executesql #SQLQUERY4, N'#TABLENAME NVARCHAR(MAX),#NUMB INT, #COL_NAME NVARCHAR(MAX) OUTPUT', #TABLENAME = #TABLENAME,#NUMB=#NUMB, #COL_NAME = #COLNAME OUTPUT
SET #SQLQUERY6 = 'SELECT ' + #COLNAME +' INTO TEST_DB..TEMP2 FROM '+ #TABLENAME
EXECUTE sp_executesql #SQLQUERY3
EXECUTE sp_executesql #SQLQUERY6
SELECT #ROWCOUNT = COUNT(*) FROM TEMP1
SELECT #ROWCOUNT2 = COUNT(*) FROM TEMP2
IF(#ROWCOUNT != #ROWCOUNT2)
BEGIN
PRINT N'NUMBER OF ROWS ARE NOT EQUAL';
BREAK
END
ELSE IF (#NUMB > #NUMB1)
BREAK
ELSE
BEGIN
SELECT * FROM TEST_DB..TEMP1
EXCEPT
SELECT * FROM TEST_DB..TEMP2
SET #NUMB = #NUMB + 1;
CONTINUE
END
END
END
END
END
It should be like this TEMP1 <--TEST_DB..TESTTABLE, TEMP2 <--TEST_DB2..TESTTABLE
But TEMP1 , TEMP2 always have the same values as TESTTABLE from the first database (TEST_DB )
It seem like I can't switch between two databases in procedure so I try to run it separate but still not work

Dynamic SQL and loop challenge, SQL Server 2012

I have n tables that is unknown before runtime with tablename always being tablename1, tablename2... tablenameN. The first column of each table is always Name. The challenge is to change that column name in each table to Name1, Name2.. NameN. I know I should be using sp_rename and a loop. Having trouble building up the query, I'm pretty new to SQL. Help would be appreciated. THanks
This should do the rename:
DECLARE #counter INT;
DECLARE #tableName NVARCHAR(100);
DECLARE #columnName NVARCHAR(100);
DECLARE #newColumnName NVARCHAR(100);
SET #counter = 1;
WHILE #counter < 65536
BEGIN
SET #tableName = 'tableName' + CAST(#counter AS NVARCHAR)
IF EXISTS(SELECT * FROM sys.tables WHERE name = #tableName)
BEGIN
SET #columnName = #tableName + N'.name';
SET #newColumnName = N'name' + CAST(#counter AS NVARCHAR);
EXEC sp_rename #objname=#columnName, #newName=#newColumnName;
END
ELSE
BEGIN
SET #counter = 65536
END
SET #counter = #counter + 1
END
It's a bit crude though.. and renames only 65535 tables and full amount only when there's none missing in between.
Uncomment sql_exec when you're sure it does what you're expecting :)
DECLARE #TableName sysname, #ColName sysname
DECLARE #num sysname
DECLARE #sql nvarchar(4000)
DECLARE cTables CURSOR FOR SELECT name from dbo.sysobjects where Category = 0 AND type NOT IN (N'F', N'FN', N'IF', N'TF', N'P', N'TR', N'V', N'K') AND name like 'tablename%'
OPEN cTables
FETCH NEXT FROM cTables INTO #TableName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #num = SUBSTRING(#Tablename, 10, 5)
SET #sql = N'sp_RENAME ''' + #TableName + '.[Name]'' , ''[Name' + #num + ']'', ''COLUMN'''
PRINT #sql
-- EXEC sp_sqlexec #sql
FETCH NEXT FROM cTables INTO #TableName
END
CLOSE cTables;
DEALLOCATE cTables;
Here's a SP - give it a try ;-)
CREATE PROCEDURE dbo.Rename
(
#n INT
)
AS
BEGIN
SET NOCOUNT ON
DECLARE #Stmt NVARCHAR(MAX)
DECLARE #i INT
DECLARE #tabname NVARCHAR(MAX)
DECLARE #colname NVARCHAR(MAX)
SET #i = 1
WHILE #i <= #n
BEGIN
SET #tabname = N'tablename' + CAST(#i AS NVARCHAR(MAX))
SET #colname = N'name' + CAST(#i AS NVARCHAR(MAX))
IF EXISTS(SELECT TOP 1 1 FROM sys.tables t WHERE t.name = #tabname)
BEGIN
SET #Stmt = N'EXEC sp_rename ''' + #tabname + '.[name]'', ''' + #colname +''',''COLUMN'''
--PRINT #Stmt
EXEC sp_executesql #Stmt
END
SET #i = #i + 1
END
END

Can I use variable in condition statement as value with where condition that test that value

I have the following stored procedure:
ALTER proc [dbo].[insertperoll] #name nvarchar(50) , #snum int , #gnum int
as
DECLARE #value nvarchar(10)
SET #value = 's'+CONVERT(nvarchar(50),#snum)
DECLARE #sqlText nvarchar(1000);
DECLARE #sqlText2 nvarchar(1000);
DECLARE #sqlText3 nvarchar(1000);
declare #g nvarchar(50) = '''g1'''
SET #sqlText = N'SELECT ' + #value + N' FROM dbo.GrideBtable'
SET #sqlText2 = ' where Gnumber = '+#g --here is the problem it error invalid column name -- the #g is value from the table condition
set #sqlText3 = #sqlText+#sqlText2
Exec (#sqlText3) -- here how can i save the result of the exec into varibale
declare #sal nvarchar(50) = #sqlText3
insert employ (name,Snumber,Gnumber,Salary) values(#name,#snum,#gnum,#sal)
QUESTION: How to put in condition variable gets value from the table when i exec it it think that the #g is column but its not its a value from the table to test it so i display one value after the exec the other QUESTION is how to save the result from the exec in variable and then use that value
I'm using SQL Server 2008 (9.0 RTM)
This will be a stored procedure
Thanks in advance
Not sure why you would go through all the loops to insert into the table where you can have a simple insert query like ..
ALTER PROC dbo.[insertperoll] #name nvarchar(50) , #snum int , #gnum int
AS
insert employ (name, Snumber, Gnumber, Salary)
select #name
, #sum
, #gnum
, case when #snum = 1 then s1
when #snum = 2 then s2
when #snum = 3 then s3
when #snum = 4 then s4
end as Salary
from dbo.GrideBtable
where Gnumber = #gnum
If your intent is to have the proc retrieve a salary value from a column determined from the parameter snum and then make an insert into employ using the values passed as parameters and the salary retrieved I think you could refactor your procedure to this:
CREATE proc [dbo].[insertperoll] #name nvarchar(50) , #snum int , #gnum int AS
DECLARE #g NVARCHAR(50) = 'g1'
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'INSERT employ (name,Snumber,Gnumber,Salary) '
SET #sql += N'SELECT ' + QUOTENAME(#name, '''')
SET #sql += N', ' + CAST(#snum AS NVARCHAR(50))
SET #sql += N', ' + CAST(#gnum AS NVARCHAR(50))
SET #sql += N', s' + CAST(#snum AS NVARCHAR(50))
SET #sql += N' FROM dbo.GrideBtable'
SET #sql += N' WHERE Gnumber = ' + QUOTENAME(#g, '''')
EXEC (#sql)
Of course you could add the #g variable to the procedure parameters instead of having it hard coded in the procedure and call it as:
EXEC insertperoll #name='john', #snum=10, #gnum=100, #g='g1'
Sample SQL Fiddle (with some assumptions made about table structure)
You could do this using sp_executesql instead of exec() since this will allow you to use parameters, you can use an output parameter to get the value from the query:
DECLARE #SQL NVARCHAR(MAX) = N'SELECT #val = ' + CONVERT(NVARCHAR(10),#snum) +
N' FROM dbo.GrideBtable WHERE Gnumber = #G1';
DECLARE #val INT; -- NOT SURE OF DATATYPE REQUIRED
EXECUTE sp_executesql #SQL, N'#G1 VARCHAR(20), #val INT OUT', 'G1', #val OUT;

What is the best approach to filter data in SQL (Dynamic Query)?

I have a stored procedure which is supposed to return a result set from a table filtered according to parameters provided.
UPDATE
alter procedure Proc_CheckExchange
#Flag varchar(3),
#symbol varchar(13)=null,
#exchange char(3)=null,
#limit money=null,
#chargerate numeric(18,4)=null,
#ChgType char(2)=null,
#IsActive int=null,
#Mkrid varchar(11)=null,
#statecode varchar(4) =null
as
declare #sql nvarchar(max)
set #sql = N'select * from Tbl_StampDutyException1 where 1 = 1'
if(#Flag='CHK')
begin
if len(isnull(#exchange, '')) > 0
set #sql = #sql + N'and exchange=#exchange'+ cast(#exchange as nvarchar(100))
if len(isnull(#symbol, '')) > 0
set #sql = #sql + N'and symbol=#symbol'+ cast(#symbol as nvarchar(100))
if len(isnull(#limit, '')) > 0
set #sql = #sql + N'and limit=#limit'+ cast(#limit as nvarchar(100))
if len(isnull(#chargerate, '')) > 0
set #sql = #sql + N'and chargerate=#chargerate'+ cast(#chargerate as nvarchar(100))
if len(isnull(#ChgType, '')) > 0
set #sql = #sql + N'and ChgType=#ChgType'+ cast(#ChgType as nvarchar(100))
if len(isnull(#IsActive, '')) > 0
set #sql = #sql + N'and IsActive=#IsActive'+ cast(#IsActive as nvarchar(100))
if len(isnull(#statecode, '')) > 0
set #sql = #sql + N'and statecode=#statecode'+ cast(#statecode as nvarchar(100))
exec (#sql)
end
if (#Flag='ALL')
begin
select * from Tbl_StampDutyException1
end
UPDATE 1
alter procedure Proc_CheckExchange
#Flag varchar(3),
#symbol varchar(13)=null,
#exchange char(3)=null,
#limit money=null,
#chargerate numeric(18,4)=null,
#ChgType char(2)=null,
#IsActive int=null,
#Mkrid varchar(11)=null,
#statecode int =null
as
declare #sql nvarchar(max)
set #sql = N'select * from Tbl_StampDutyException1 where 1 = 1'
if(#Flag='CHK')
begin
if len(isnull(#exchange, '')) > 0
set #sql = #sql + N' and exchange = #exchange'
if len(isnull(#limit, '')) > 0
set #sql = #sql + N' and limit = #limit'
if len(isnull(#chargerate, '')) > 0
set #sql = #sql + N' and chargerate = #chargerate'
if len(isnull(#ChgType, '')) > 0
set #sql = #sql + N' and ChgType = #ChgType'
if len(isnull(#IsActive, '')) > 0
set #sql = #sql + N' and IsActive = #IsActive'
if len(isnull(#statecode, '')) > 0
set #sql = #sql + N' and statecode = #statecode'
if len(isnull(#symbol, '')) > 0
set #sql = #sql + N' and symbol = #symbol'
declare #params as nvarchar(max) = N'#Flag varchar(3),
#symbol varchar(13),
#exchange char(3),
#limit money,
#chargerate numeric(18,4),
#ChgType char(2),
#IsActive int,
#Mkrid varchar(11),
#statecode varchar(4)'
print #sql
--EXECUTE sp_executesql #sql, #params, #Flag, #symbol, #exchange, #limit, #chargerate, #ChgType, #IsActive, #Mkrid, #statecode
end
I am trying to create a stored procedure in which there will be as many conditions in WHERE clause as passed to the stored procedure. I hope I am clear about what I am trying to achieve. I am getting error Error converting data type varchar to numeric.
I think that a good solution would be this one
SELECT * from dbo.Clients
WHERE
(#param1 IS NULL OR field1 = #param1)
AND (#param2 IS NULL OR field2 = #param2)
With this approach the statement will not be re-processed (execution plan) by the server every time you execute the query.
What you could do is to rewrite your stored proc using dynamic SQL and include parts of where clause only if parameters are defined, e.g. replace the body with
declare #sql nvarchar(max)
set #sql = N'select * from Tbl_StampDutyException1 where 1 = 1'
if len(isnull(#exchange, '')) > 0
set #sql = #sql + N' and exchange = ' + cast(#exchange as nvarchar(100))
-- add all parameters; you need to cast them to nvarchar if they have other type
exec (#sql)
As an improvement, you can use sp_executesql to execute dynamic SQL. See here on how to use it. In this case, the code will be:
declare #sql nvarchar(max)
set #sql = N'select * from Tbl_StampDutyException1 where 1 = 1'
if len(isnull(#exchange, '')) > 0
set #sql = #sql + N' and exchange = #exchange'
-- add all parameters;
declare #params as nvarchar(max) = N'#Flag varchar(3),
#symbol varchar(13),
#exchange char(3),
#limit money,
#chargerate numeric(18,4),
#ChgType char(2),
#IsActive int,
#Mkrid varchar(11),
#statecode varchar(4)'
EXECUTE sp_executesql #sql, #params, #Flag, #symbol, #exchange, #limit, #chargerate, #ChgType, #IsActive, #Mkrid, #statecode
By the way, don't use select * in stored procedures, it's not a good practice. List all the columns you want to return. Otherwise, if the table definition changes, you will get different result to what it was previously.
What I guess you need is a CONDITIONAL WHERE CLAUSE approach, have provided the sample script below, you need to convert all the IF conditions to a CASE and you then you can combine all the conditions into a single SELECT statement:
SELECT * FROM Tbl_StampDutyException1
WHERE
exchange = CASE WHEN #exchange='' or #exchange is null THEN exchange ELSE #exchange END
AND symbol = Case when #symbol='' or #symbol is null THEN symbol ELSE symbol
AND *so on.....*