Generic DELETE UPDATE with TRY CATCH - sql-server-2008

I'm trying run this procedure with generic parameters.
If I can't delete because some foreign key, it should update the row.
But when I execute it, still running forever and don't complete the process, any Idea?
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER procedure [dbo].[stp_Batch_Registros_Desativados_Excluir]
#table_name VARCHAR(100),
#id int
AS
BEGIN
DECLARE #column VARCHAR(100),
#sql VARCHAR(300);
SET #column = (SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLUMNPROPERTY(OBJECT_ID(TABLE_NAME),COLUMN_NAME,'IsIdentity') = 1
AND TABLE_NAME = #table_name);
BEGIN TRY
select #sql = 'DELETE ' + #table_name + ' WHERE ' + #column + ' = ' + CONVERT(VARCHAR,#id);
exec(#sql);
END TRY
BEGIN CATCH
select #sql = 'UPDATE ' + #table_name + ' SET fg_excluido = 2 WHERE ' + #column + ' = ' + CONVERT(VARCHAR,#id) ;
exec(#sql);
END CATCH;
END

This seems to work fine for me. Do you have triggers, cascading foreign keys, etc.?
USE tempdb;
GO
CREATE TABLE dbo.fooblat1
(
id INT IDENTITY(1,1) PRIMARY KEY,
x VARCHAR(1),
fg_excluido TINYINT NOT NULL DEFAULT 1
);
CREATE TABLE dbo.fooblat2
(
blatid INT NOT NULL FOREIGN KEY REFERENCES dbo.fooblat1(id)
);
INSERT dbo.fooblat1(x) SELECT 'a';
INSERT dbo.fooblat2 SELECT SCOPE_IDENTITY();
INSERT dbo.fooblat1(x) SELECT 'b';
GO
Then:
CREATE PROCEDURE [dbo].[proc]
#table_name VARCHAR(100),
#id INT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #column NVARCHAR(100),
#sql NVARCHAR(MAX);
SELECT #column = name
FROM sys.columns
WHERE [object_id] = OBJECT_ID(#table_name)
AND is_identity = 1;
BEGIN TRY
SET #sql = 'DELETE [' + #table_name
+ '] WHERE [' + #column + '] = '
+ CONVERT(VARCHAR(12),#id);
EXEC sp_executesql #sql;
END TRY
BEGIN CATCH
SET #sql = 'UPDATE [' + #table_name
+ '] SET fg_excluido = 2 WHERE '
+ #column + ' = ' + CONVERT(VARCHAR(12),#id);
EXEC sp_executesql #sql;
END CATCH;
END
GO
Then:
EXEC [dbo].[proc] 'fooblat1', 1; -- should update
GO
EXEC [dbo].[proc] 'fooblat1', 2; -- should delete
GO
SELECT * FROM dbo.fooblat1;
GO
Cleanup:
DROP PROC [dbo].[proc];
DROP TABLE dbo.fooblat2;
DROP TABLE dbo.fooblat1;
GO

Related

select from unknown number of tables with parameter

I want to select (union) rows from multiple tables using Parameter
I have table w with two columns:
The column table_name is referring to other tables in my DB, and condition is the 'where' that should be added to the query.
table_name | condition
---------------------
x | y=2
x | r=3
t | y=2
the query should be something like:
select * from x where y=2
union
select * from x where r=3
union
select * from t where y=2
of course that the number of unions is unknown.
Should it be stored procedure? cursor?
One way to get this done. Initial answer was SQL Server syntax. This edit has the MySQL syntax. Make sure your temp table cannot be accessed at the same time. E.g. In MySQL temp tables are unique to the connection. Also add your error checking. In MySQL set the appropriate varchar size for your needs. I used 1024 across the board just for testing purposes.
MySQL syntax
CREATE table test (
id int,
table_name varchar(1024),
where_c varchar(1024)
);
INSERT into test(id, table_name, where_c) values
(1,'x','y=2'),
(2,'x','r=3'),
(3,'t','y=2');
DROP PROCEDURE IF EXISTS generate_sql;
DELIMITER //
CREATE PROCEDURE generate_sql()
BEGIN
DECLARE v_table_name VARCHAR(1024);
DECLARE v_where_c VARCHAR(1024);
DECLARE table_id INT;
DECLARE counter INT;
DECLARE v_SQL varchar(1024);
CREATE TEMPORARY table test_copy
SELECT * FROM test;
SET v_SQL = '';
SET counter = (SELECT COUNT(1) FROM test_copy);
WHILE counter > 0 DO
SELECT id, table_name, where_c
INTO table_id, v_table_name, v_where_c
FROM test_copy LIMIT 1;
SET v_SQL = CONCAT(v_SQL, 'SELECT * FROM ', v_table_name, ' WHERE ', v_where_c);
DELETE FROM test_copy WHERE id=table_id;
SET counter = (SELECT COUNT(1) FROM test_copy);
IF counter > 0 THEN
SET v_SQL = CONCAT(v_SQL,' UNION ');
ELSE
SET v_SQL = v_SQL;
END IF;
END WHILE;
DROP table test_copy;
SELECT v_SQL;
END //
DELIMITER ;
call generate_sql()
SQL Server syntax
CREATE table test (
id int,
table_name varchar(MAX),
condition varchar(MAX)
);
INSERT into test(id, table_name, condition) values
(1,'x','y=2'),
(2,'x','r=3'),
(3,'t','y=2');
SELECT * INTO #temp FROM test;
DECLARE #SQL varchar(MAX);
SET #SQL='';
while exists (select * from #temp)
begin
DECLARE #table_name varchar(MAX);
DECLARE #condition varchar(MAX);
DECLARE #table_id int;
SELECT top 1 #table_id=id, #table_name=table_name, #condition=condition FROM #temp;
SET #SQL += 'SELECT * FROM ' + #table_name + ' WHERE ' + #condition;
delete #temp where id = #table_id;
if exists (select * from #temp) SET #SQL += ' UNION ';
end
SELECT #SQL;
drop table #temp;
Assuming that tables x and t have the same definition and that you want to ignore duplicate results by using UNION rather than UNION ALL, the following should work:
SET #sql = '';
SELECT GROUP_CONCAT(
CONCAT('SELECT * FROM `', `table_name`, '` WHERE ', `condition`)
SEPARATOR ' UNION ') INTO #sql
FROM w;
SET #sql = CONCAT('SELECT * FROM ( ', #sql, ' ) a;');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
(I've edited your question slightly because you had two different definitions for table x)

How can i concat the values in MySql?

Please find the below code, The below code is sql server code[Stored Procedure] i am converting sql to mysql in this link mysql and below is the sql SP
USE databasename
GO
ALTER PROCEDURE GetSbsberIDbyDept
#P_DepartmentIds varchar(max),
#OP_ID varchar(max) output
AS
BEGIN
Declare #Position int
Declare #length int
Declare #value varchar(8000)
Declare #SubscribrIDs varchar(max)
Declare #FinalSubscriberids varchar(max) = ''
SET #Position = 0
SET #length = 0
select #P_DepartmentIds
WHILE CHARINDEX(',', #P_DepartmentIds, #Position+1)>0
BEGIN
set #Length = CHARINDEX(',', #P_DepartmentIds, #Position+1) - #Position
set #value = SUBSTRING(#P_DepartmentIds, #Position, #length)
select #SubscribrIDs = STUFF((SELECT ', ' + cast(SubscriberId as varchar(max)) from SbsberDetails
where Deptid = #value and ParentId is null order by SubscriberId asc FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)') ,1,2,'')
if(#SubscribrIDs is not null)
begin
set #FinalSubscriberids += cast(#SubscribrIDs as varchar(max)) + ',';
end
set #Position = CHARINDEX(',', #P_DepartmentIds, #Position + #length) +1
END
SET #OP_ID = #FinalSubscriberids
END
And i converted to MySql but i am getting the error in STUFF statement like STUFF is not valid input at this position, below is the Mysql code
USE databasename
DELIMITER //
CREATE PROCEDURE GetSbsberIDbyDept(
p_P_DepartmentIds longtext,
p_OP_ID out longtext )
BEGIN
Declare v_Position int;
Declare v_length int;
Declare v_value varchar(8000);
Declare v_SubscribrIDs longtext;
Declare v_FinalSubscriberids longtext Default '';
SET v_Position = 0;
SET v_length = 0;
select p_P_DepartmentIds
WHILE; CHARINDEX(',', p_P_DepartmentIds, v_Position+1)>0
BEGIN
set v_length = CHARINDEX(',', p_P_DepartmentIds, v_Position+1) - v_Position;
set v_value = SUBSTRING(p_P_DepartmentIds, v_Position, v_length);
set v_SubscribrIDs = STUFF((SELECT Concat(', ' , cast(SubscriberId as longtext)) from SbsberDetails
where Deptid = v_value and ParentId is null order by SubscriberId asc FOR XML; PATH(''), TYPE).value('.', 'NVARCHAR(MAX)') ,1,2,'')
if(v_SubscribrIDs is not null)
then
set v_FinalSubscriberids += concat(cast(v_SubscribrIDs as longtext) , ',');
end if;
set v_Position = CHARINDEX(',', p_P_DepartmentIds, v_Position + v_length) +1;
END;
SET p_OP_ID = v_FinalSubscriberids;
END;
//
DELIMITER ;
How do i convert the above sql code to MySql?
STUFF is SQL server function so it's thrown error in mysql, The thing that i got your are trying to row concat but in mysql there is funtion name GROUP_CONCAT for group cancating ,so you can use it instead of stuff

Dropping A Table Using Dynamic SQL

This presents an error of:
Msg 207, Level 16, State 1, Line 1
Invalid column name 'Testingwithdynamicsql'.
Now why is that, if I print my sql it appears valid to me, what do I alter to make it work?
Declare #tablename varchar(500), #sql varchar(max)
set #tablename = 'Testingwithdynamicsql'
set #sql = 'IF OBJECT_ID('+#tablename+', ''U'') IS NOT NULL
DROP TABLE ' + #tablename + ' '
Print #sql
exec(#sql)
The #tablename needs to be quoted
Declare #tablename varchar(500), #sql varchar(max)
set #tablename = 'Testingwithdynamicsql'
set #sql = 'IF OBJECT_ID('''+#tablename+''', ''U'') IS NOT NULL
DROP TABLE ' + #tablename + ' '
Print #sql

how to pass table name into a function?

is there any method how to make MySQL function that receives table name or field name ?
something like this :
CREATE PROCEDURE delete_row(the_id INT UNSIGNED , #table_name )
BEGIN
IF ....... THEN
BEGIN
DELETE FROM #table_name WHERE id = the_id ;
.............
END
END
I tested it with string (SET #table_name="table_name"), but it doesn't works.
declare varchar(max) #mySQL
set #mySQL = 'DELETE FROM ' + #tablename + 'WHERE id = ' + Convert(varchar, #the_id)
sp_executeSQL #mySQL
and to make this work on MySQL (as the commenters pointed out) it should look like this:
mysql> prepare stmt from -> 'DELETE FROM ' + #tablename + 'WHERE id = ' + Convert(varchar, #the_id)
mysql> execute stmt;

SQL Server: Can you help me with this query?

I want to run a diagnostic report on our SQL Server 2008 database server.
I am looping through all of the databases, and then for each database, I want to look at each table. But, when I go to look at each table (with tbl_cursor), it always picks up the tables in the database 'master'.
I think it's because of my tbl_cursor selection :
SELECT table_name FROM information_schema.tables WHERE table_type = 'base table'
How do I fix this?
Here's the entire code:
SET NOCOUNT ON
DECLARE #table_count INT
DECLARE #db_cursor VARCHAR(100)
DECLARE database_cursor CURSOR FOR
SELECT name FROM sys.databases where name<>N'master'
OPEN database_cursor
FETCH NEXT FROM database_cursor INTO #db_cursor
WHILE ##Fetch_status = 0
BEGIN
PRINT #db_cursor
SET #table_count = 0
DECLARE #table_cursor VARCHAR(100)
DECLARE tbl_cursor CURSOR FOR
SELECT table_name FROM information_schema.tables WHERE table_type = 'base table'
OPEN tbl_cursor
FETCH NEXT FROM tbl_cursor INTO #table_cursor
WHILE ##Fetch_status = 0
BEGIN
DECLARE #table_cmd NVARCHAR(255)
SET #table_cmd = N'IF NOT EXISTS( SELECT TOP(1) * FROM ' + #table_cursor + ') PRINT N'' Table ''''' + #table_cursor + ''''' is empty'' '
--PRINT #table_cmd --debug
EXEC sp_executesql #table_cmd
SET #table_count = #table_count + 1
FETCH NEXT FROM tbl_cursor INTO #table_cursor
END
CLOSE tbl_cursor
DEALLOCATE tbl_cursor
PRINT #db_cursor + N' Total Tables : ' + CAST( #table_count as varchar(2) )
PRINT N'' -- print another blank line
SET #table_count = 0
FETCH NEXT FROM database_cursor INTO #db_cursor
END
CLOSE database_cursor
DEALLOCATE database_cursor
SET NOCOUNT OFF
The problem is because you're actually always running the INFORMATION_SCHEMA.TABLES query under the master db context.
You'd need to convert the tbl_cursor block into dynamic SQL in order to fully qualify the query with the DB name.
e.g.
SELECT table_name FROM YourDatabase.INFORMATION_SCHEMA.TABLES WHERE....
is essentially what you need to be executing for that cursor.
It's easier to use table variables so you can add rows to #tablist using another dynamic SQL statement
SET NOCOUNT ON
DECLARE #table_count INT
DECLARE #dblist TABLE (DBName VARCHAR(100))
DECLARE #tablist TABLE (TableName VARCHAR(100))
DECLARE #dbname varchar(100), #tabname varchar(100)
INSERT #dblist
SELECT name FROM sys.databases where name<>N'master'
SELECT TOP 1 #dbname = DBName FROM #dblist
WHILE ##ROWCOUNT <> 0
BEGIN
INSERT #tablist (tableName)
EXEC ('SELECT table_name FROM ' + #dbname + '.information_schema.tables WHERE table_type = ''base table'' ')
SELECT TOP 1 #tabname = tableName FROM #tablist
WHILE ##ROWCOUNT <> 0
BEGIN
--do my stuff
DELETE #tablist WHERE tableName = #tabname
SELECT TOP 1 #tabname = tableName FROM #tablist
END
DELETE #dblist WHERE DBName = #dbname
SELECT TOP 1 #dbname = DBName FROM #dblist
END
You might have to create dynamic SQL. because information_schema will fetch objects only from the current active database against which you are running this query.
you can try sys.objects