If statement not working properly in sql server query - sql

I have a table where I want to run following query
IF COL_LENGTH('table_name','IsDeleted') IS NOT NULL
BEGIN
IF COL_LENGTH('table_name','IsActive') IS NOT NULL
BEGIN
UPDATE table_name
SET IsActive = ~IsDeleted
END
ELSE
BEGIN
EXEC sp_RENAME 'table_name.IsDeleted', 'IsActive', 'COLUMN'
UPDATE table_name
SET IsActive = ~IsActive
END
ALTER TABLE table_name
DROP CONSTRAINT DF_table_name_IsDeleted
ALTER TABLE table_name DROP COLUMN IsDeleted
END
ELSE
BEGIN
--IsDeleted column does not exist
END
In my table there is no column with name "IsDeleted", so it shouldn't enter in the first if statement. But somehow it enters the first if statement in gives me error message:
Invalid column name 'IsDeleted'."
at line SET IsActive = ~IsDeleted
What is wrong in my query? Why control is not going in else part. Is there any syntax error?
IsActive and IsDeleted are of type BIT

This is because SQL Server parses and validates the statement regardless of the IF statements, meaning it parses and validates the whole batch. So when parsing the statement:
UPDATE table_name SET IsActive = ~IsDeleted
it errors out saying:
Invalid column name IsDeleted
See this example:
IF 1 = 1 BEGIN
CREATE TABLE #temp(col INT)
END
ELSE BEGIN
CREATE TABLE #temp(col INT)
END
It produces an error:
There is already an object named '#temp' in the database.
The workaround is to use Dynamic SQL.
DECLARE #sql NVARCHAR(MAX)
IF COL_LENGTH('table_name','IsDeleted') IS NOT NULL BEGIN
IF COL_LENGTH('table_name','IsActive') IS NOT NULL BEGIN
SET #sql = 'UPDATE table_name SET IsActive = ~IsDeleted'
EXEC sp_executesql #sql
END
ELSE BEGIN
EXEC sp_RENAME 'table_name.IsDeleted', 'IsActive', 'COLUMN'
SET #sql = 'UPDATE table_name SET IsActive = ~IsActive'
EXEC sp_executesql #sql
END
....
END

Related

Does sp_executesql support multiple values in one parameter and return multiple records?

I have created a stored procedure as shown below, but it's returning only one row instead of 3:
CREATE PROCEDURE [dbo].[tempsp]
(#RecycleIds NVARCHAR(MAX) = NULL)
AS
BEGIN
DECLARE #Err INT
DECLARE #WhereClause NVARCHAR(MAX)
DECLARE #SQLText1 NVARCHAR(MAX)
DECLARE #SQLText NVARCHAR(MAX)
SET #SQLText1 = 'SELECT FROM dbo.SKU '
IF #RecycledSkuIds IS NOT NULL
BEGIN
SET #SQLText = 'SELECT FROM dbo.SKU WHERE SKU.SkuId IN (#RecycleIds)'
EXEC sp_executesql #SQLText, N'#RecycleSkuIds nvarchar', #RecycleIds
END
ELSE
BEGIN
EXEC(#SQLText1)
END
SET #Err = ##ERROR
RETURN #Err
END
-------end of stored procedure--------
EXEC tempsp #RecycleIds = '5,6,7'
After running this SQL statement, it only returns one row instead of 3, with the id's of 5, 6, 7.
Can anyone tell me what I am doing wrong?
i wanted to use sp_executesql, so that it can be safe against sql injection with strong type defined.
Use a table type parameter, with a strongly typed column:
CREATE TYPE dbo.IDs AS table (ID int);
GO
CREATE PROCEDURE [dbo].[tempsp] #RecycleIds dbo.IDs READONLY AS
BEGIN
IF EXISTS (SELECT 1 FROM #RecycleIds)
SELECT * --Replace with needed columns
FROM dbo.SKU S
--Using EXISTS in case someone silly puts in the same ID twice.
WHERE EXISTS (SELECT 1
FROM #RecycleIds R
WHERE R.ID = S.SkuID);
ELSE
SELECT * --Replace with needed columns
FROM dbo.SKU S
END;
GO
Then you could execute it like so:
EXEC dbo.tempsp; --All Rows
GO
DECLARE #RecycleIds dbo.IDs;
INSERT INTO #RecycleIds
VALUES(1),(40),(182);
EXEC dbo.tempsp #RecycleIds;
I was trying to retrive the rows whose id matches within the IN clause.
SET #INClauseIds='''' + replace(#Ids, ',', ''',''') + ''''
Above statement would convert the ID's ='1,2,3' to '1','2','3' which i can directly place in the IN clause.
SET #SQLText1 ='EXEC(''SELECT Name,SEOFriendlyName FROM SKU Where Id IN ( ''+ #Ids+'' ) )'
EXEC sp_executesql #SQLText1 ,N'#INClauseIds nvarchar(max)',#Ids=#INClauseIds
If you want to avoid the usage of Temp Table which would add extra caliculation time. you can you the above strategy to retrive n number of records. Safe with strongly coupled with sp_executesql and without any sql injection.
You cannot use IN. Or, more accurately, you have a string and you are confusing it with a list. One method is to instead use LIKE:
SET #SQLText = '
SELECT *
FROM dbo.SKU
WHERE CONCAT('','', #RecycleIds, '','') LIKE CONCAT(''%,'', SKU.SkuId, '',%'')
';

Table name variable in MS SQL query

I have dynamically created tables, like XXX_JOURNAL.
Where XXX - is table prefix (variable), and _JOURNAL - is constant in table name.
I need create UPDATE trigger on database, not on particular table, and use table name (prefix) as variable:
CREATE TRIGGER triggerName ON %_JOURNAL
FOR UPDATE
AS
UPDATE XXX_JOURNAL
SET COMPANY_ID = LEFT(tableName,3) //tableName = current table (XXX_JOURNAL)
WHERE ID = ID FROM inserted
So here I have two difficulties:
How to create one trigger for all tables LIKE %_JOURNAL?
How to use table name as the keyword for current table?
I know there are a lot of mistakes in syntax. For example, I cannot use '%_JOURNAL' as table name on trigger creation. It's just for explanation, that I need create one trigger for all dynamically created tables in future.
Any ideas?
You can use stored procedure with dynamic SQL:
CREATE PROCEDURE TriggerCreationForJournals
#XXX as nvarchar(3)
AS
BEGIN
DECLARE #sql nvarchar(max),
#triggerName nvarchar(max) = #XXX + N'_JOURNAL_UPDATE',
#objectCheck int,
#checkSQL nvarchar(max),
#params nvarchar(max) = N'#objectCheck int OUTPUT'
SELECT #checkSQL = N'SELECT #objectCheck = OBJECT_ID(N'''+#triggerName+''')'
EXEC sp_executesql #checkSQL, #params, #objectCheck = #objectCheck OUTPUT
IF #objectCheck IS NULL
BEGIN
SELECT #sql = N'
CREATE TRIGGER '+QUOTENAME(#triggerName)+' ON ['+#XXX+'_JOURNAL]
FOR UPDATE
AS
UPDATE x
SET COMPANY_ID = '''+#XXX+'''
FROM ['+#XXX+'_JOURNAL] x
INNER JOIN inserted i
ON i.ID = x.ID'
EXEC sp_executesql #sql
END
ELSE
BEGIN
PRINT 'Trigger '+QUOTENAME(#triggerName)+' already exists'
END
END
Then run this:
DECLARE #sql nvarchar(max)
SELECT #sql = (
SELECT 'EXEC TriggerCreationForJournals '''+LEFT([name],3) +''';' +CHAR(10)
FROM sys.tables
WHERE [name] LIKE '%JOURNAL'
FOR XML PATH('')
)
EXEC sp_executesql #sql
To create triggers for all tables.
In #sql there will be query like:
EXEC TriggerCreationForJournals 'AFG';
EXEC TriggerCreationForJournals 'DFG';
The purpose of stored procedure is to check if trigger on table exists - if so skip its creation, you can modify the SP to drop them if exists.
The second part is a creation of script and running the SP for all tables you need.
Hope, this answer helps you with your questions.

SQL Server stored procedure error . invalid identifier

I have a stored procedure, but when I execute it from my front-end I get this error:
The name 'CREATE TABLE tmp_148_58 (affili_item_id
varchar(250),academic_id varchar(250),college_id
varchar(250),item_value_quantity_college_entry
varchar(250),item_value_notes_college_entry
varchar(250),college_enter_on varchar(250),college_enter_by
varchar(250),affili_category_colleges_autoid varchar(20))' is not a
valid identifier.
My procedure code:
ALTER PROCEDURE [dbo].[SpPortal_AppForAffi_Upd_Both_Lbl_And_Vals1]
(#columnList TEXT
,#insScript nvarchar(1000)
,#collegeId INT
,#LoginId BIGINT)
AS
BEGIN
DECLARE
#tmpTableName VARCHAR(200),
#effectCount INT = 0,
#effectCountTotal INT = 0,
#ExeQuery nvarchar(1000),
#InsertQuery nvarchar(1000)
SET #tmpTableName = CONCAT('#tmp_',#collegeId,'_',#LoginId);
SET #ExeQuery = CONCAT('DROP TABLE IF EXISTS ', #tmpTableName);
EXECUTE #ExeQuery ;
-- create temp table.. --
SET #ExeQuery = CONCAT ('CREATE TABLE ' , #tmpTableName , ' (',#columnList,')' ) ; -- here column list should be come from froent end...
EXECUTE #ExeQuery;
-- # create temp table.. --
-- load data into temp table --
SET #InsertQuery = CONCAT(' ' , #insScript);
EXECUTE #InsertQuery;
-- # load data into temp table.. --
-- updating affili_items_colleges master table--
SET #effectCount=0;
-- SET param_sp_success=0;
Begin TRANSACTION
Begin Try
-- SET param_sp_success = 0;
SET #effectCount = 0;
SET #effectCountTotal = 0;
SET #ExeQuery = CONCAT(' UPDATE ', #tmpTableName,' AS tmp ,affili_item_label afil,affili_items afi
SET afil.item_lable_name = tmp.item_value_quantity_college_entry
,afil.enter_on=tmp.college_enter_on
,afil.enter_by= tmp.college_enter_by
WHERE tmp.affili_item_id=afil.affili_item_id AND tmp.affili_item_label_id = afil.affili_item_label_id
AND afi.is_label = 1 AND tmp.academic_id=afil.academic_id AND tmp.college_id=afil.college_id
AND tmp.affili_item_id = afi.affili_item_id AND afi.active_status = 1 ');
EXECUTE #ExeQuery;
SET #ExeQuery = CONCAT(' UPDATE ', #tmpTableName,' AS tmp ,affili_items_colleges afic,affili_items afi
SET afic.item_value_quantity_college_entry = tmp.item_value_quantity_college_entry
,afic.item_value_notes_college_entry=tmp.item_value_notes_college_entry
,afic.college_enter_on=tmp.college_enter_on
,afic.college_enter_by= tmp.college_enter_by
WHERE tmp.affili_item_id=afic.affili_item_id AND tmp.affili_item_label_id = afic.affili_item_label_id
AND tmp.academic_id=afic.academic_id AND tmp.college_id=afic.college_id
AND tmp.affili_item_id = afi.affili_item_id AND afi.is_label <> 1 AND afi.active_status = 1 ');
EXECUTE #ExeQuery;
declare #te int=0
SET #ExeQuery = CONCAT ('SELECT COUNT(tem.affili_item_id) INTO #te
FROM ',#tmpTableName,' tem INNER JOIN affili_items afi ON tem.affili_item_id = afi.affili_item_id AND afi.is_label <> 1
WHERE afi.active_status = 1 ') ;
EXECUTE #ExeQuery;
SET #effectCount=0;
SET #effectCount = #te ;
IF(#effectCount>0)
BEGIN
SET #effectCountTotal= #effectCount+1;
END
-- SET param_sp_success = effectCountTotal;
IF(##TRANCOUNT>0)
BEGIN
COMMIT TRANSACTION
END
ELSE
BEGIN
ROLLBACK TRANSACTION
END
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
END CATCH
END
Can anyone help me out in solving it? I converted above query from mysql to SQL Server.
First of all - I really wondering why you are using all of this dynamically created statements. As I can see from your script - the only reason for it is unique name of temporary table you're creating.
But you don't really need your temporary table to have unique name, as this table is visible only in the scope of stored procedure where it was created (and also in the scope of 'child' procedures called from that one).
Also, as per your error it looks like your script tries to create real, not temporary table - see CREATE TABLE tmp_148_58 - name of table doesn't contains #. So you may have no right to create real tables under account you've running your sp.
I suggest you to rewrite your code without that confusing dynamics and error should go away ;)

SQL Server : error on column add

I am trying to copy rows from one column to another. Execution flow is simple.
Check if column is exist.
If not - add column
Define cursor and populate rows for new column
Set column to NOT NULL.
Unfortunately I am getting this type of error. It says that column does not exist. But I've created it and committed transaction. What could be a reason? Thanks!
Msg 207, Level 16, State 1, Line 29
Invalid column name 'Field_Name'.
Code:
begin transaction;
IF NOT EXISTS(SELECT * FROM sys.columns
WHERE Name = N'Field_Name' and Object_ID = Object_ID(N'dbo.Table_Name'))
BEGIN
ALTER TABLE [dbo].[Table_Name]
ADD [Field_Name] VARCHAR(255)
END
commit;
DECLARE
#var1 int,
#var2 varchar(255)
DECLARE copy_cursor CURSOR FOR
SELECT id, Name
FROM Table_Name
OPEN copy_cursor
FETCH NEXT FROM copy_cursor
INTO #var1, #var2
WHILE ##FETCH_STATUS = 0
BEGIN
UPDATE Table_Name
SET Field_Name = #var2
WHERE id = #var1
FETCH NEXT FROM copy_cursor
INTO #var1, #var2
END
CLOSE copy_cursor;
DEALLOCATE copy_cursor;
ALTER TABLE Table_Name ALTER COLUMN Field_name varchar(255) NOT NULL
The server tries to compile an entire batch of SQL before it runs any of it. So, before referencing the column in a query, you need to create it (if necessary) in a separate batch. Insert a GO keyword to instruct your tool (SQLCMD, OSQL, SSMS) to send separate batches:
begin transaction;
IF NOT EXISTS(SELECT * FROM sys.columns
WHERE Name = N'Field_Name' and Object_ID = Object_ID(N'dbo.Table_Name'))
BEGIN
ALTER TABLE [dbo].[Table_Name]
ADD [Field_Name] VARCHAR(255)
END
commit;
GO
I think you may also need to wrap the ALTER as follows, to deal with the case where the column does exist:
EXEC sp_executesql 'ALTER TABLE [dbo].[Table_Name]
ADD [Field_Name] VARCHAR(255)'
First check if the table exists, start and commit the transaction, and it should work.
BEGIN TRANSACTION
IF (Object_ID(N'dbo.Table_Name') > 0)
BEGIN
IF NOT EXISTS(SELECT * FROM sys.columns
WHERE Name = N'Field_Name' and Object_ID = Object_ID(N'dbo.Table_Name'))
BEGIN
ALTER TABLE [dbo].[Table_Name]
ADD [Field_Name] VARCHAR(255)
END
END
COMMIT
GO

Trying to update all string values of 'NULL' to NULL in sql server

I have a database table that was imported from a csv file which had NULL in it, so that when it was imported instead of fields being NULL they contain the string value 'NULL'.
The CSV file is to big to open in a text editor to edit out all of the NULL so I am trying to create a SQL query to update each column of the table.
So far I have this
Alter PROCEDURE [dbo].[sp_fixnulls]
#column nvarchar(100)
AS
BEGIN
SET NOCOUNT ON;
UPDATE my_table
SET #column=''
WHERE #column = 'NULL'
END
---------------
sp_fixnulls (SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME ='my_table')
but this is not working. I get the error message
Msg 201, Level 16, State 4, Procedure sp_fixnulls, Line 0
Procedure or function 'sp_fixnulls' expects parameter '#column', which was not supplied.
#column is a variable. It cannot dynamically swap out its contents into SQL. You need to use TSQL to accomplish this, whereby you generate the SQL into a string then execute it. Such as:
DECLARE #sql VARCHAR(MAX)
SET #sql = '
UPDATE my_table
SET ' + #column + '=''''
WHERE ' + #column + ' = ''NULL''
'
EXEC (#sql) -- don't forget the parentheses
A small note that my code sets the column to empty string, which is NOT the same as NULL. I went with your existing example. If you want NULL, then
SET ' + #column + '= NULL
'Msg 201, Level 16, State 4, Procedure sp_fixnulls, Line 0 Procedure or function 'sp_fixnulls' expects parameter '#column', which was not supplied.'
The error you're getting is because you're trying to pass a select statement as a parameter to a stored procedure. If you were to pass just the name of one column to the stored procedure, you would get past that error.
Then you wouldn't get another error, but you wouldn't get the result you want. If you fix your stored procedure as Eli recommended (with the addition of parentheses around #sql in the EXEC statement, as I commented), it will work.
Then you'll need to wrap your stored procedure with a cursor (which most people don't recommend, but works when needed). Alternatively, you can just select all of the column names and generate a bunch of execute statements, and then run the statements.
SELECT 'EXECUTE sp_fixnulls N''' + column_name + ''';'
FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'my_table';
This will generate statements like the following (assuming the table has columns id, name, and date).
EXECUTE sp_fixnulls N'id';
EXECUTE sp_fixnulls N'name';
EXECUTE sp_fixnulls N'date';
Another option is to forgo the stored procedure altogether.
SELECT 'UPDATE my_table SET ' + column_name
+ ' = NULL WHERE ' + column_name + ' = ''NULL'';'
FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'my_table';
This will generate statements like the following.
UPDATE my_table SET id = NULL WHERE id = 'NULL';
UPDATE my_table SET name = NULL WHERE name = 'NULL';
UPDATE my_table SET date = NULL WHERE date = 'NULL';