Conditional script with for non existing column (yet)? - sql-server

I'm using SQL server for creating script to run in production .
If a table doesn't contain a specific column, then create that column.
I've already did that :
IF NOT EXISTS (
SELECT 1
FROM INFORMATION_SCHEMA.columns
WHERE COLUMN_NAME = 'SeeMaxDaysBackwardPrice'
AND TABLE_NAME = 'tblsubscriptiontype'
)
BEGIN
ALTER TABLE dbo.tblsubscriptiontype ADD SeeMaxDaysBackwardPrice INT NULL
END
But now I want to insert a new row to that modified table . So now my script looks like this :
IF NOT EXISTS (
SELECT 1
FROM INFORMATION_SCHEMA.columns
WHERE COLUMN_NAME = 'SeeMaxDaysBackwardPrice'
AND TABLE_NAME = 'tblsubscriptiontype'
)
BEGIN
ALTER TABLE dbo.tblsubscriptiontype ADD SeeMaxDaysBackwardPrice INT NULL
SET IDENTITY_INSERT [dbo].[tblSubscriptionType] ON;
BEGIN TRANSACTION
INSERT INTO [dbo].[tblSubscriptionType]
(
...
[SeeMaxDaysBackwardPrice] <-------- ERROR
)
SELECT ...
-365
COMMIT;
RAISERROR (
N'[dbo].[tblSubscriptionType]: Insert Batch: 1.....Done!',
10,
1
)
WITH NOWAIT;
SET IDENTITY_INSERT [dbo].[tblSubscriptionType] OFF;
END
GO
But now I get an error ( which I perfectlly understand) :
Msg 207, Level 16, State 1, Line 29
Invalid column name 'SeeMaxDaysBackwardPrice'.
Sure I can split the script into 2 seperate scripts , but then I'll have this condition twice :
IF NOT EXISTS (
SELECT 1
FROM INFORMATION_SCHEMA.columns
WHERE COLUMN_NAME = 'SeeMaxDaysBackwardPrice'
AND TABLE_NAME = 'tblsubscriptiontype'
)
Question:
Is there any way to make SQL more relaxed about a column that do not exists yet ? (I already know about dynamic query (text) , but I wonder if there is another option.

Just wrap the code in dynamic T-SQL statement:
IF NOT EXISTS (
SELECT 1
FROM INFORMATION_SCHEMA.columns
WHERE COLUMN_NAME = 'SeeMaxDaysBackwardPrice'
AND TABLE_NAME = 'tblsubscriptiontype'
)
BEGIN
ALTER TABLE dbo.tblsubscriptiontype ADD SeeMaxDaysBackwardPrice INT NULL
SET IDENTITY_INSERT [dbo].[tblSubscriptionType] ON;
DECLARE #DynamicTSQLStatement NVARCHAR(MAX);
SET #DynamicTSQLStatement = N'
INSERT INTO [dbo].[tblSubscriptionType]
(
...
[SeeMaxDaysBackwardPrice]
)
SELECT ...
-365
';
BEGIN TRY
BEGIN TRAN;
EXEC sp_executesql #DynamicTSQLStatement;
COMMIT TRAN;
END TRY
BEGIN CATCH
ROLLBACK TRAN;
END CATCH
SET IDENTITY_INSERT [dbo].[tblSubscriptionType] OFF;
END
GO

Related

How many tables altered by EXEC in SQL Server?

I am changing the data type of columns in a few tables. and I have created a dynamic query for the same. Now I want to know the count of the tables which got altered actually by this query. I want to compare the expected and actual value if it matches then good else I need to run the rollback, Is there a way to do this?
Query:
USE FXecute
GO
DECLARE #ExpectedCounter INT=0, #ActualCounter AS INT=0
DROP TABLE IF EXISTS #List
CREATE TABLE #List (Command varchar(max), OrderBy INT IDENTITY(1,1))
INSERT INTO #List
SELECT
'ALTER TABLE ['+TABLE_SCHEMA+'].['+TABLE_NAME+'] ALTER COLUMN ['+COLUMN_NAME+'] DECIMAL(22,6)' AS 'Queries'
FROM FXecute.INFORMATION_SCHEMA.COLUMNS
WHERE DATA_TYPE = 'DECIMAL' and (column_name LIKE '%amount%' or column_name LIKE '%amt%' OR column_name LIKE '%total%'
OR column_name LIKE '%USD%') and TABLE_NAME NOT LIKE 'syncobj%'
SET #ActualCounter = ##ROWCOUNT;
PRINT 'Expected tables to be altered: ' + + CAST(#ActualCounter AS NVARCHAR(10))
DECLARE #sqlcmd VARCHAR(MAX);
SET #sqlcmd = (
SELECT STRING_AGG(Command,';' + CHAR(10)) WITHIN GROUP (ORDER BY [OrderBy]) as cmd
FROM #List
)
PRINT #sqlcmd
EXEC(#sqlcmd);
IF (#ExpectedCounter = #ActualCounter)
BEGIN
PRINT 'All Good'
END
ELSE
BEGIN
PRINT 'Something wrong'
--Rollback Script to be run
END
GO
Edit:
ALTER TABLE [dbo].[Table1] ALTER COLUMN [Column1] DECIMAL(22,6);
ALTER TABLE [dbo].[Table2] ALTER COLUMN [Column2] DECIMAL(22,6);
ALTER TABLE [dbo].[Table2] ALTER COLUMN [Column3] DECIMAL(22,6);
ALTER TABLE [dbo].[Table3] ALTER COLUMN [Column4] DECIMAL(22,6);

Check if DB Exists in a Trigger

I have an SQL Trigger that is dependent on a separate database in the same server, but the issue arises when one wants to use the database independently, in which case the second database will usually not be migrated. Meaning that, if a query were to be executed that would activate the Trigger, it will fail due to requiring that second database.
I attempted to circumvent the issue by covering the entire trigger with a script that checks if the database exists, but even when I do include it, it will basically check the entire trigger and will fail anyway. Below is what happened when I inserted a record in an SQL server without the 2nd database.
Msg 2702, Level 16, State 2, Procedure tChange2ndDB, Line 22 [Batch Start Line 0]
Database '2ndDB' does not exist.
Here's what my current (basic) code looks like:
CREATE TRIGGER [dbo].[tChange2ndDB]
ON [dbo].[crelign]
AFTER INSERT,DELETE,UPDATE
AS
BEGIN
IF (EXISTS (SELECT name FROM master.dbo.sysdatabases WHERE ('[' + name + ']' = '[2ndDB]' OR name = '[2ndDB]')))
BEGIN
SET NOCOUNT ON;
BEGIN TRY
DECLARE #insCount INT
DECLARE #delCount INT
DECLARE #Code VARCHAR(5)
DECLARE #CodeUpd VARCHAR(5)
DECLARE #Description VARCHAR(50)
SET #insCount = (SELECT COUNT(*) FROM INSERTED)
SET #delCount = (SELECT COUNT(*) FROM DELETED)
;IF (EXISTS (SELECT name FROM master.dbo.sysdatabases WHERE ('[' + name + ']' = '[2ndDB]' OR name = '[2ndDB]')))
ALTER TABLE [2ndDB].[dbo].Field DISABLE TRIGGER [tChange1stDB];
-- * Other code here * --
;IF (EXISTS (SELECT name FROM master.dbo.sysdatabases WHERE ('[' + name + ']' = '[2ndDB]' OR name = '[2ndDB]')))
ALTER TABLE [2ndDB].[dbo].Field ENABLE TRIGGER [tChange1stDB];
END TRY
BEGIN CATCH
-- * Error Handling --
DECLARE #ErrMsg NVARCHAR(MAX), #ErrorSeverity INT, #ErrorState INT;
SELECT #ErrorSeverity = ERROR_SEVERITY(), #ErrorState = ERROR_STATE();
SET #ErrMsg = (SELECT 'TR : tChange2ndDB Line : ' + RTRIM(CONVERT(VARCHAR(MAX), ERROR_LINE())) + ' - ' + ERROR_MESSAGE());
RAISERROR(#ErrMsg, #ErrorSeverity, #ErrorState);
END CATCH
END
END
GO
What is the best solution to overcome this issue?
Thank you for reading.
You can try adding an Else and then use RETURN 0 or '' to dont do your code.

batch compress all tables in SQL Server

Running into a problem with the code below. I'm trying to batch compress all tables in a certain database, but there's an issue with my syntax...
USE backups
GO
DECLARE #tables TABLE ( TABLE_NAME VARCHAR(MAX) )
INSERT INTO #tables ( TABLE_NAME ) SELECT DISTINCT TABLE_NAME FROM information_schema.TABLES
WHILE ( SELECT COUNT(*) ct FROM #tables ) > 0
BEGIN
DECLARE #table VARCHAR(MAX) = ( SELECT TOP 1 TABLE_NAME FROM #tables )
DELETE FROM #tables WHERE TABLE_NAME = #table
BEGIN TRY
ALTER TABLE #table REBUILD PARTITION = ALL WITH ( DATA_COMPRESSION = PAGE )
END TRY
BEGIN CATCH
RAISERROR( 'Compression failed for backup table : %s', 20, 101, #table ) WITH LOG
END CATCH
END
The error I'm getting is:
Incorrect syntax near '#table'. Expecting '.', ID, or QUOTED_ID
Turns out I found that you can't use variable names for tables or other schema objects in some situations. This fixes the problem...
USE backups
GO
DECLARE #tables TABLE ( TABLE_NAME NVARCHAR(MAX) )
DECLARE #query NVARCHAR(MAX)
INSERT INTO #tables ( TABLE_NAME ) SELECT DISTINCT TABLE_NAME FROM information_schema.TABLES
WHILE ( SELECT COUNT(*) ct FROM #tables ) > 0
BEGIN
DECLARE #table VARCHAR(MAX) = ( SELECT TOP 1 TABLE_NAME FROM #tables )
DELETE FROM #tables WHERE TABLE_NAME = #table
BEGIN TRY
SET #query = N'ALTER TABLE ' + #table + N' REBUILD PARTITION = ALL WITH ( DATA_COMPRESSION = PAGE )'
EXEC sp_executesql #query
END TRY
BEGIN CATCH
RAISERROR( 'Compression failed for backup table : %s', 20, 101, #table ) WITH LOG
END CATCH
END
I did a slight update in line 3 by adding where TABLE_TYPE= 'BASE TABLE' at the end, so the full line 3 looks like:
INSERT INTO #tables ( TABLE_NAME ) SELECT DISTINCT TABLE_NAME FROM information_schema.TABLES where TABLE_TYPE= 'BASE TABLE'

WHILE SQL Server error

I have a SQL query like this :
ALTER PROCEDURE [dbo].[sp_dynamic_column_list](
#tahun varchar(4),
#bulan varchar(2),
#pks varchar(3))
AS
BEGIN
SET NOCOUNT ON;
DECLARE #totalrow int
DECLARE #inc int = 1
DECLARE #dynamictable NVARCHAR(MAX)
CREATE TABLE #temp
(
tanggal datetime,
)
-- query cari column dulu baru alter table temp diatas
SET #totalrow = dbo.fn_count_row_penerimaan(2014,11,40)
WHILE (#inc <= #totalrow)
BEGIN
ALTER TABLE #temp ADD #inc FLOAT
SET #inc = #inc + 1
END
INSERT INTO #temp
EXEC sp_get_list_penerimaan_pks2 #tahun, #bulan, #pks
SELECT * FROM #temp
DROP TABLE #temp
END
I got error like this:
[Err] 42000 - [SQL Server]Incorrect syntax near '#inc'.
I'm new to SQL Server and like to know the solution for this problem
Thanks in advance
ALTER TABLE my_table ADD #column INT
You need to use Execute statement as mentioned in link.
WHILE #inc <= #totalrow
BEGIN
exec ('ALTER table #temp add '+#inc+' FLOAT set '+#inc+' = '+ #inc+'+1')
END
In a SQL statment variable value cannot be provided as a column name, to achieve this you have to use a dynamic SQL query like the Following:
WHILE (#inc <= #totalrow)
BEGIN
Declare #strquery as varchar(4000)
SET #strquery = 'ALTER TABLE #temp ADD [' + #inc + '] FLOAT'
EXECUTE sp_executesql #strquery -- if #inc =1 then #strQuery : ALTER TABLE #temp ADD [1] FLOAT
SET #inc = #inc + 1
END
You can read more about Dynamic SQL queries in this Article

Getting rows affected in stored procedure INSERT statement with NOCOUNT OFF

I have a stored procedure in SQL Server 2014 where I first delete data from a table and execute another stored procedure as shown below. I don't care about rows affected on this code so I use SET NOCOUNT ON. I then perform an Insert statement first setting SET NOCOUNT OFF. I would expect the stored procedure to return the rows affected automatically from the INSERT but that has not proven to be the case. I searched SO and found the ##ROWCOUNT and that is working. However, is that really required? Why wouldn't the INSERT statement in the code below return the records affected?
ALTER PROCEDURE [dbo].[SaveTextSetting]
#UserId INT ,
#SettingType INT = 0 ,
#SettingValue NVARCHAR(MAX)
AS
BEGIN
BEGIN
SET NOCOUNT ON;
DELETE FROM Settings
WHERE UserId = #UserId
AND SettingType = #SettingType;
EXEC dbo.PurgeExpiredSettings;
END;
BEGIN
SET NOCOUNT OFF;
INSERT INTO dbo.Settings
( UserId ,
SettingsText ,
SettingType
)
VALUES ( #UserId ,
#SettingValue ,
#SettingType
);
RETURN ##ROWCOUNT; --without this we don't get the rows affected
END;
END;
Are you sure?
CREATE PROCEDURE spTest AS RETURN
GO
ALTER PROCEDURE spTest
AS
BEGIN
SET NOCOUNT OFF
DECLARE #rt int
CREATE TABLE #t (
v int NOT NULL
)
INSERT INTO #t (v)
SELECT v FROM (VALUES (1), (1), (2)) AS t (v)
SET #rt = ##ROWCOUNT
DROP TABLE #t
RETURN #rt
END
GO
DECLARE #rt int
EXEC #rt = spTest
SELECT #rt
GO
DROP PROCEDURE spTest
GO
I would use an output param something like.....
ALTER PROCEDURE [dbo].[SaveTextSetting]
#UserId INT ,
#SettingType INT = 0 ,
#SettingValue NVARCHAR(MAX),
#RowCount INT OUTPUT
AS
BEGIN
SET NOCOUNT ON;
DELETE FROM Settings
WHERE UserId = #UserId
AND SettingType = #SettingType;
EXEC dbo.PurgeExpiredSettings;
INSERT INTO dbo.Settings( UserId ,SettingsText ,SettingType)
VALUES ( #UserId ,#SettingValue ,#SettingType);
SET #RowCount= ##ROWCOUNT;
END

Resources