SQL - Add column and set value after IF NOT EXISTS - sql

I'm having trouble with an Alter Table if a column doesn't exist.
This is my code:
DECLARE #appId INT
DECLARE #cursor CURSOR
IF NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE COLUMN_NAME = 'MinMktIdeaParticipants' AND TABLE_NAME = 'mkIdeaCategories')
BEGIN
ALTER TABLE mkIdeaCategories
ADD MinMktIdeaParticipants int NOT NULL DEFAULT 1
END
IF EXISTS(SELECT Value FROM appConfiguration WHERE CodeId = 16 AND AppConfigurationTypeId = 3 AND ModuleId = 10)
BEGIN
SET #cursor = CURSOR FAST_FORWARD
FOR
SELECT ApplicationId FROM mkMarket
OPEN #cursor
FETCH NEXT FROM #cursor INTO #appId
WHILE (##Fetch_Status >= 0)
BEGIN
UPDATE
mkIdeaCategories
SET MinMktIdeaParticipants = (SELECT Value FROM appConfiguration
WHERE ApplicationId = #appId AND CodeId = 16 AND AppConfigurationTypeId = 3 AND ModuleId = 10)
WHERE IdeaCatId IN
(select distinct(theme.IdeaCatId) from dbo.mkIdeaCategories theme
inner join dbo.mkMarketIdeaCategories mic ON theme.IdeaCatId = mic.IdeaCatId
inner join mkMarket m on m.MarketId=mic.MarketId
WHERE m.ApplicationId = #appId)
FETCH NEXT FROM #cursor INTO #appId
END
CLOSE #cursor
DEALLOCATE #cursor
DELETE * FROM appConfiguration WHERE CodeId = 16 AND AppConfigurationTypeId = 3 AND ModuleId = 10
END
I don't think there are any errors. For some reason, the output error is
Msg 207, Level 16, State 1, Line 53
Invalid column name 'MinMktIdeaParticipants'.
I've already searched for similar questions, but couldn't find an answer that solved the problem.

When SQL Server processes a script, there are two phases. The first phase is compilation. The second is execution.
The error that you are getting is a compilation error. All the code is compiled, regardless of the if conditions. So, you are getting an error because the column doesn't exist. The column wouldn't be created until the execution phase.
One solution is to change the second part of the code to dynamic SQL, using exec (or better yet exec sp_executesql) to execute the code.

Gordon Linoff's solution will work. Another way to achieve what you want is to wrap your whole statement in a stored procedure.
this will fail:
select * from nonexisting_table
this will create a stored proc:
create procedure nonsense
as
begin
select * from nonexisting_table
end
BUT there are errors in your query:
SET #cursor = CURSOR FAST_FORWARD
To create a fast forward cursor in sql server you have to declare it.
Thats my favourite example from mssqltips.com:
DECLARE db_cursor CURSOR FAST_FORWARD FOR
SELECT name
FROM MASTER.dbo.sysdatabases
WHERE name NOT IN ('master','model','msdb','tempdb')
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #name
WHILE ##FETCH_STATUS = 0
BEGIN
SET #fileName = #path + #name + '_' + #fileDate + '.BAK'
BACKUP DATABASE #name TO DISK = #fileName
FETCH NEXT FROM db_cursor INTO #name
END
CLOSE db_cursor
DEALLOCATE db_cursor

Related

Delete multiple records from table through cursor in sql server

there are number of test IP's which I would like to remove through system defined sp
exec sp_delete_firewall_rule from sys.firewall_rules table in sql server
I am using below cursor but its not working
declare #name nvarchar(max)
declare cur CURSOR LOCAL for
select #name from sys.firewall_rules where [name] like '%TestIP%'
open cur
fetch next from cur into #name
while ##FETCH_STATUS = 0 BEGIN
exec sp_delete_firewall_rule #name
fetch next from cur into #name
END
close cur
deallocate cur
It worked for me, you just need to change a couple of things in your code.
In the select list include the table ColumnName [name] instead of variable. You did not pass any value to the variable so this gives a NULL result.
Include SP parameter while executing exec sp_delete_firewall_rule #name = #name1;
I have these IP’s in my firewall rules:
With the below code I am deleting the IP’s which has a name like TestIP1.
DECLARE #name1 nvarchar(128);
DECLARE MyCursor CURSOR FOR
SELECT [name] from sys.firewall_rules where [name] like '%TestIP1%';
OPEN MyCursor;
FETCH FROM MyCursor into #name1
WHILE ##FETCH_STATUS = 0 BEGIN
EXEC sp_delete_firewall_rule #name = #name1 ;
FETCH next from MyCursor into #name1
END
CLOSE MyCursor;
DEALLOCATE MyCursor;
GO
Now the result shows only 1 IP which is not included in the above delete list.

Using SELECT INTO for a multi-table query

I've been using a script that a coworker showed me to query against all of our databases. It starts with CREATE TABLE #tempTable and then I need to define all the columns. I was wondering if it was possible to not define the table and use SELECT * INTO instead.
Current Query:
CREATE TABLE #tempTable(category nvarchar(50), eventType nvarchar(50), occurtime datetime);
DECLARE #fixedname nvarchar(150);
DECLARE dbCursor CURSOR FOR
SELECT REPLACE(QUOTENAME(name),'''','''''')
FROM master.sys.databases
WHERE state_desc = 'ONLINE'
AND HAS_DBACCESS(name) = 1
AND name NOT IN ('master', 'msdb','model','tempdb');
OPEN dbCursor
FETCH NEXT FROM dbCursor INTO #fixedname;
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC ('USE ' + #fixedname + ';' +
'INSERT INTO #tempTable
SELECT
DISTINCT UserChangeEvent.category [Category],
UserChangeEvent.eventType [EventType],
UserChangeEvent.occurTime[DateTime]
FROM
UserChangeEvent
WHERE
/* User Conditions */
(
(eventType LIKE ''Alarm'')
)
AND UserChangeEvent.occurtime >= ''2021-01-01''
;');
FETCH NEXT FROM dbCursor INTO #fixedname;
END;
CLOSE dbCURSOR
DEALLOCATE dbCursor
SELECT * FROM #tempTable
--The following line can be commented out if you plan to do multiple queries within the
DROP TABLE #tempTable`
What I'm hoping might work:
DECLARE #fixedname nvarchar(150);
DECLARE dbCursor CURSOR FOR
SELECT REPLACE(QUOTENAME(name),'''','''''')
FROM master.sys.databases
WHERE state_desc = 'ONLINE'
AND HAS_DBACCESS(name) = 1
AND name NOT IN ('master', 'msdb','model','tempdb');
OPEN dbCursor
FETCH NEXT FROM dbCursor INTO #fixedname;
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC ('USE ' + #fixedname + ';' +
'SELECT * INTO #tempTable
;');
FETCH NEXT FROM dbCursor INTO #fixedname;
END;
CLOSE dbCURSOR
DEALLOCATE dbCursor
SELECT * FROM #tempTable
WHERE
/* User Conditions */
(
(eventType LIKE ''Alarm'')
)
AND UserChangeEvent.occurtime >= ''2021-01-01''
--The following line can be commented out if you plan to do multiple queries within the
DROP TABLE #tempTable
I also read something that suggests I might not have to drop the table at the end since it's a temp table.

Can I create a table with columns named similar to field value of another table, return by SELECT statement

Question Image
When I execute this script, there is a syntax error. What is wrong with this?
use TEST
go
CREATE TABLE newTagsTable
(
(SELECT TEST.dbo.dynamicTags.Alais
FROM TEST.dbo.dynamicTags
WHERE ID = 2) varchar(200)
);
Is this what you need ?
SELECT Alais
into newTagsTable
FROM dynamicTags
WHERE ID = 2;
DEMO
Ok, so something like this then:
create procedure test_proc
as
declare #p_sql varchar(2000);
declare #p_sql_2 varchar(2000);
declare #getid CURSOR;
SET #getid = CURSOR FOR
SELECT Alais
FROM dynamicTags;
begin
set #p_sql = 'create table newTagsTable (';
OPEN #getid
FETCH NEXT
FROM #getid INTO #p_sql_2
WHILE ##FETCH_STATUS = 0
begin
set #p_sql = #p_sql + #p_sql_2 + ' varchar(20),'
FETCH NEXT FROM #getid INTO #p_sql_2
end;
set #p_sql = left(#p_sql, len(#p_sql)-1) + ')';
EXEC (#p_sql);
end;
And then just execute the procedure:
exec test_proc
You can add parameters to this to make it more usable for orher situations...
Here is the demo for this second option:
DEMO

Script -> Query for multiple databases

I have the following problem: I want to execute a query on multiple databases on my SQL Server. Every customer has a separate database. Those all have exactly the same table and their names are similar. So there is a database kde_01_Miller, then a kde_02_Mueller and so on ...
I want to execute a query in every one of those databases.
Here's what I have tried:
DECLARE #name VARCHAR(100) -- database name
DECLARE #dothis nvarchar(200)
DECLARE db_cursor CURSOR FOR
SELECT name
FROM master.dbo.sysdatabases
WHERE name like 'kde_0%'
order by name
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #name
WHILE ##FETCH_STATUS = 0
BEGIN
set #dothis = 'use [' + #name + ']'
exec sp_executesql #dothis
/* Start query */
select description from dbo.basicdata
/* End query */
FETCH NEXT FROM db_cursor INTO #name
END
CLOSE db_cursor
DEALLOCATE db_cursor
The problem is that the query does not work properly. The use statement seems not to be working. I get a result for every database I have, but the result is always the same one, dependent on the database I'm currently doing a query for.
I've also tried the following and it worked: Instead of my while-loop I did this:
WHILE ##FETCH_STATUS = 0
BEGIN
set #dothis= 'select description from ' + QUOTENAME(#name) + '.dbo.basicdata'
exec sp_executesql #dothis
FETCH NEXT FROM db_cursor INTO #name
END
But I don't like this way, because you need the quotename(#name) for every table.
How do I make the first example work?
That's not possible, since sp_executesql is executed as its own self-contained batch, that mean you did actually "use" other databases, but only in those batchs i mentioned earlier
I'll try to be more clear, this code of you is a batch, since there's no "GO" command inside (read my sql comments) :
DECLARE #name VARCHAR(100) -- database name
DECLARE #dothis nvarchar(200)
DECLARE db_cursor CURSOR FOR
SELECT name
FROM master.dbo.sysdatabases
WHERE name like 'kde_0%'
order by name
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #name
WHILE ##FETCH_STATUS = 0
BEGIN
set #dothis = 'use [' + #name + ']'
-- this will create another batch and execute the #dothis
-- it'll have nothing todo with your current executing batch,
-- which is calling the sp_executesql
exec sp_executesql #dothis
/* Start query */
select description from dbo.basicdata
/* End query */
FETCH NEXT FROM db_cursor INTO #name
END
CLOSE db_cursor
DEALLOCATE db_cursor
So, there's only one way left, write whatever you want to do with the database inside the #dothis :
declare #dothis nvarchar(max)
set #dothis = '
use [' + #name + ']
-- query start
Select description from dbo.basicdata
-- query end
'
exec sp_executesql #dothis
While this question has already been answered, I thought I'd provide a second answer I believe is better. Instead of using a Cursor you can generate dynamic SQL to query multiple databases.
DECLARE #sql NVARCHAR(Max);
SELECT #sql = COALESCE(#sql, '') + 'SELECT * FROM ' + [name] + '.sys.tables' + CHAR(13)
FROM sys.databases
PRINT #sql
EXEC sp_executesql #sql
The above SQL will Generate the following SQL.
SELECT * FROM master.sys.tables
SELECT * FROM tempdb.sys.tables
SELECT * FROM model.sys.tables
SELECT * FROM msdb.sys.tables
SELECT * FROM StackOverflow.sys.tables
SELECT * FROM AdventureWorks2012.sys.tables
SELECT * FROM AdventureWorksDW2012.sys.tables
As you see I was able to run a query against multiple databases. I could even UNION the data together if I'd like.
though already answered in finding a solution for the same issue I wrote this query.
it provides the data and it shows the source of the database which provides the answer.
note the order by is only to show double values but slows down the result
declare #results table (
name varchar(50),
clientnr numeric(20) ,
dbname_source varchar(20)
);
insert #results
exec sp_msforeachdb N'
use [?]
if left(''?'',3) = ''ons'' -- only execute the query against databases that match the naming pattern in this example starting with ons (use your own database names make sure to exclude systemdatabases
--and ''?'' <> ''MIS_MTA''
begin
--select script that to be executed over multiple databases
select distinct
c.name,
c.identificationNo as numeric,
d.table_catalog
from [?].dbo.clients as c , [?].INFORMATION_SCHEMA.COLUMNS as d
where 1=1
and isnumeric(c.identificationNo) = 1
end;
';
select * from #results
where
isnumeric(clientnr) = 1
order by 2
;
example result:
name clientnr dbname_source
TestclientA 9000 OnsDB
TestclientA 9000 OnsDB_Fixed
Storcken 9999 OnsDB_Fixed
Storcken 9999 OnsDB

EXEC within the TRY...CATCH block

I am trying to drop all the tables in the database with the following script:
WHILE EXISTS(SELECT * FROM sys.tables where is_ms_shipped = 0)
BEGIN
EXEC sp_MSforeachtable 'DROP TABLE ?'
END
I am getting lots of errors because of the foreign key constraints. But that is fine, tables are dropped anyway. I would like to get rid of the error messages with the following script.
WHILE EXISTS(SELECT * FROM sys.tables where is_ms_shipped = 0)
BEGIN
BEGIN TRY
EXEC sp_MSforeachtable 'DROP TABLE ?';
END TRY
BEGIN CATCH
END CATCH
END
This script just runs forever trying to drop the first table.
What am I doing wrong?
I'm not sure what's wrong with your query, but I've dropped tables using a cursor like this:
DECLARE #name as varchar(100)
DECLARE MyCursor CURSOR FAST_FORWARD FOR
SELECT name FROM sys.tables
OPEN MyCursor
FETCH NEXT FROM MyCursor INTO #name
WHILE ##FETCH_STATUS = 0
BEGIN
execute('drop table ' +#name)
FETCH NEXT FROM MyCursor INTO #name
END
CLOSE MyCursor
DEALLOCATE MyCursor