I want to search in table for all records with a special characters - char(11), char(7) etc.
I have found one stored procedure which helps me to find it. But it is not accepting the input parameters as follows:
EXEC sp_FindStringInTable '%'+char(7)+'%', 'CPOA-TALENTLink-Test-Leeds', 'TALENT_Contact_ChangeLog'
Error:
Msg 102, Level 15, State 1, Line 1
Incorrect syntax near '+'.
Stored procedure:
CREATE PROCEDURE dbo.sp_FindStringInTable
#stringToFind NVARCHAR(100),
#schema SYSNAME,
#table SYSNAME
AS
BEGIN TRY
DECLARE #sqlCommand VARCHAR(MAX) = 'SELECT * FROM [' + #schema + '].[' + #table + '] WHERE '
SELECT #sqlCommand = #sqlCommand + '[' + COLUMN_NAME + '] LIKE ''' + #stringToFind + ''' OR '
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = #schema
AND TABLE_NAME = #table
AND DATA_TYPE IN ('char','nchar','ntext','nvarchar','text','varchar')
SET #sqlCommand = LEFT(#sqlCommand, LEN(#sqlCommand) - 3)
EXEC (#sqlCommand)
PRINT #sqlCommand
END TRY
BEGIN CATCH
PRINT 'There was an error. Check to make sure object exists.'
PRINT error_message()
END CATCH
As mentioned in error, I'm unable to search through table for special characters.
In calls to procedures expressions other than literals or variables don't work.
Assign the concatenation to a variable and pass that variable to the procedure.
DECLARE #p varchar(max) = '%' + char(7) + '%';
EXEC sp_FindStringInTable #p, 'CPOA-TALENTLink-Test-Leeds', 'TALENT_Contact_ChangeLog';
Literal string concatenation like '[' + #schema + ']' and ' LIKE ''' + #stringToFind + '''' isn't safe. Far from it. I suspect that parametrising your query is going to fix this:
CREATE PROCEDURE dbo.sp_FindStringInTable #stringToFind NVARCHAR(100), #schema sysname, #table sysname
AS
BEGIN TRY
DECLARE #sqlCommand varchar(max);
SET #sqlCommand = N'SELECT *' + NCHAR(10) + --Formatting yoru dynamic SQL is a very good idea
N'FROM ' + QUOTENAME(#schema) + N'.' + QUOTENAME(#table) + NCHAR(10) +
N'WHERE' +
STUFF((SELECT NCHAR(10) + N' AND ' + QUOTENAME(COLUMN_NAME) + N'LIKE #String'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = #schema
AND TABLE_NAME = #table
AND DATA_TYPE IN ('char','nchar','ntext','nvarchar','text','varchar')
FOR XML PATH(N'')),1,6,N'')
PRINT #sqlCommand; --your best friend
EXEC sp_executesql #sqlCommand, N'String nvarchar(100)', #String = #stringToFind;
END TRY
BEGIN CATCH
PRINT 'There was an error. Check to make sure object exists.'
PRINT error_message()
END CATCH
Note that I have not tested the above. Your best friend is there to help you debug.
This question already has answers here:
SQL Server: Get table primary key using sql query [duplicate]
(10 answers)
How to find out whether a table has some unique columns
(5 answers)
Closed 6 years ago.
I need to check if a column is candidate key. To make it generic, I created a stored procedure:
ALTER PROCEDURE [dbo].[CheckPK]
(
#tableName VARCHAR(100),
#pk VARCHAR(100)
)
AS
PRINT 'Checking for candidate key ' + #pk + ' for table: ' + #tableName
DECLARE #sql NVARCHAR(4000)
SET #sql = 'select count(distinct ([' + #pk + '])) as tot_pk from ' + #tableName + ' select count (*) as tot_real from ' + #tableName
EXEC sp_executesql #sql
Which works fine. What I have to do is verify that the two selected values are the same.
QUESTION: is it possible to add an if(tot_pk = tot_real) type of condition?
I am a newbie in SQL, so I apologise if this question seems trivial. Thank you
Try this code. Maybe is that you need.
I added an optional parameter #Schema.
CODE
--EXEC [dbo].[CheckPK] 'name of table','name of column', 'schema is optional'
ALTER PROCEDURE [dbo].[CheckPK]
(
#tableName VARCHAR(100)
, #pk VARCHAR(100)
, #Schema VARCHAR(100) = NULL
)
AS
BEGIN
BEGIN TRY
PRINT 'Checking primary key ' + #pk + ' for table: ' + #tableName
DECLARE #sql NVARCHAR(4000)
SET #sql = 'select count(distinct ([' + #pk + '])) as tot_pk from '
+ ISNULL(#Schema + '.', '') + #tableName + ' select count (*) as tot_real from ' + ISNULL(#Schema + '.', '') + #tableName
EXEC sp_executesql #sql
IF EXISTS (
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE OBJECTPROPERTY(OBJECT_ID(CONSTRAINT_SCHEMA + '.' + CONSTRAINT_NAME), 'IsPrimaryKey') = 1
AND TABLE_NAME = #tableName
AND TABLE_SCHEMA = ISNULL(#Schema, TABLE_SCHEMA)
AND COLUMN_NAME = #pk
)
SELECT #pk + ' IS A PRYMARY KEY'
ELSE
SELECT #pk + ' IS NOT A PRYMARY KEY'
END TRY
BEGIN CATCH
SELECT 'An error has occurred. Verify that ''' + #tableName + ''' it is a table and has the correct schema and ''' + #pk + ''' is a valid column of the table'
END CATCH
END
I wish to modify strings in several columns (for example all columns containing the 'sound' string), for example replacing ',' by '.'. Further to this post, I understand I have to use dynamic SQL. I created the following procedure:
USE [myDB]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[RemoveStringInColumn] (#colName varchar(50), #tableName varchar(50), #to_remove varchar(50), #to_add varchar(50))
AS
DECLARE #sql nvarchar(4000)
SET #sql = 'UPDATE ' + #tableName + ' SET ' + #colName + ' = REPLACE(' + #colName + ',' + #to_remove + ','+ #to_add + ');'
PRINT #sql
EXEC sp_executesql #sql
Which is called by:
EXEC dbo.RemoveStringInColumn 'COL_1', 'TABLE_1', ',', '.'
1) The problem is the #sql command does not contain the little hyphen arond the comma and the dot. How can I solve this?
2) In this post they use a SELECT command to fetch all column names. So far, I managed to fetch column names containing 'sound'.
select COLUMN_NAME AS my_cols
from INFORMATION_SCHEMA.COLUMNS
where table_name = 'TABLE_1' AND COLUMN_NAME LIKE '%sound%'
How can I put column names into a list and use a for loop to go through them calling the RemoveStringInColumn procedure?
Thanks
Just double the single quotes around #to_remove and #to_add
DECLARE #sql NVARCHAR(4000)
SET #sql = 'UPDATE ' + Quotename(#tableName) + ' SET ' + Quotename(#colName)
+ ' = REPLACE(' + Quotename(#colName) + ',''' + #to_remove + ''','''
+ #to_add + ''');'
PRINT #sql
EXEC Sp_executesql
#sql
Update : To do the replace for more than one column
DECLARE #sql NVARCHAR(4000),
#col_list VARCHAR(8000)= ''
SET #col_list = (SELECT ',' + Quotename(COLUMN_NAME) + ' = REPLACE('
+ Quotename(COLUMN_NAME) + ',''' + #to_remove
+ ''',''' + #to_add + ''')'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'TABLE_1'
AND COLUMN_NAME LIKE '%sound%'
FOR xml path(''))
SET #col_list = Stuff(#col_list, 1, 1, '')
SELECT #col_list
SET #sql = 'UPDATE ' + Quotename(#tableName) + ' SET '
+ #col_list
PRINT #sql
EXEC Sp_executesql
#sql
This is probably a bit of a limited, but valuable scenario. I have a SQL Server 2008 database with a table that has millions of records. There appears to be an intermittent problem with several of the records. I'm trying to repro the problem. In an effort to do this, I finally got the ID of an offending record. I would like to generate an INSERT statement associated with this single record in my PROD database. Then I can easily migrate it into my TESTING database in an effort to repro and resolve the problem.
Basically, I need to generate a single INSERT statement for a single record from a single table where I know the primary key value of the record.
Does anyone have any ideas of how I can accomplish this? Essentially, I want to generate insert statements on a conditional basis.
Thank you!
First try to recreate what you want to insert with a SELECT statement.
After that you can insert into the table with a INSERT INTO like this:
INSERT INTO tablename
SELECT ....
If they are on different servers, you can use INSERT like this:
INSERT INTO tablename VALUES (...)
using the values given by the SELECT in the other server fill the values in the insert.
In your specific case I think you can do this:
CREATE PROCEDURE dbo.GenerateSingleInsert
#table NVARCHAR(511), -- expects schema.table notation
#pk_column SYSNAME, -- column that is primary key
#pk_value INT -- change data type accordingly
AS
BEGIN
SET NOCOUNT ON;
DECLARE #cols NVARCHAR(MAX), #vals NVARCHAR(MAX),
#valOut NVARCHAR(MAX), #valSQL NVARCHAR(MAX);
SELECT #cols = N'', #vals = N'';
SELECT #cols = #cols + ',' + QUOTENAME(name),
#vals = #vals + ' + ' + REPLICATE(CHAR(39),3) + ','
+ REPLICATE(CHAR(39),3) + ' + ' + REPLICATE(CHAR(39),2) + '+'
+ 'RTRIM(' + CASE WHEN system_type_id IN (40,41,42,43,58,61) THEN
'CONVERT(CHAR(8), ' + QUOTENAME(name) + ', 112) + '' ''
+ CONVERT(CHAR(14), ' + QUOTENAME(name) + ', 14)'
ELSE 'REPLACE(' + QUOTENAME(name) + ','''''''','''''''''''')' END + ')
+ ' + REPLICATE(CHAR(39),2)
FROM sys.columns WHERE [object_id] = OBJECT_ID(#table)
AND system_type_id <> 189 -- can't insert rowversion
AND is_computed = 0; -- can't insert computed columns
SELECT #cols = STUFF(#cols, 1, 1, ''),
#vals = REPLICATE(CHAR(39), 4) + ' + ' + STUFF(#vals, 1, 13, '')
+ REPLICATE(CHAR(39), 2);
SELECT #valSQL = N'SELECT #valOut = ' + #vals + ' FROM ' + #table + ' WHERE '
+ QUOTENAME(#pk_column) + ' = ''' + RTRIM(#pk_value) + ''';';
EXEC sp_executesql #valSQL, N'#valOut NVARCHAR(MAX) OUTPUT', #valOut OUTPUT;
SELECT SQL = 'INSERT ' + #table + '(' + #cols + ') SELECT ' + #valOut;
END
GO
So let's try it out:
CREATE TABLE dbo.splunge
(
ID INT, dt DATETIME, rv ROWVERSION, t NVARCHAR(MAX)
);
INSERT dbo.splunge(ID, dt, t)
SELECT 1, GETDATE(), 'foo'
UNION ALL SELECT 2, GETDATE(), 'bar'
UNION ALL SELECT 3, GETDATE(), 'O''Brien';
EXEC dbo.GenerateSingleInsert N'dbo.splunge', N'ID', 1;
SQL
-------------
INSERT dbo.splunge([ID],[dt],[t]) SELECT '1','20120517 10:07:07:330','foo'
EXEC dbo.GenerateSingleInsert N'dbo.splunge', N'ID', 2;
SQL
-------------
INSERT dbo.splunge([ID],[dt],[t]) SELECT '2','20120517 10:07:07:330','bar'
EXEC dbo.GenerateSingleInsert N'dbo.splunge', N'ID', 3;
SQL
-------------
INSERT dbo.splunge([ID],[dt],[t]) SELECT '3','20120517 10:07:07:330','O''Brien'
If there is an IDENTITY column you may need to set SET IDENTITY_INSERT ON for the TEST table, and verify that there is no collision. Probably about 500 caveats I should mention, I haven't tested all data types, etc.
However in the more general case there is a lot more to it than this. Vyas K has a pretty robust stored procedure that should demonstrate how complicated it can get:
http://vyaskn.tripod.com/code/generate_inserts_2005.txt
You are probably far better off using a tool like Red-Gate's SQL Data Compare to pick a specific row and generate an insert for you. As I've blogged about, paying for a tool is not just about the money, it's about the hours of troubleshooting and bug-fixing that someone else has already done for you.
Aaron,
I liked your code, it solved a problem for me. I ran into a few issues using it (like you said I would) with nulls and the text type so I made some changes to address those issues.
ALTER PROCEDURE dbo.GenerateSingleInsert
#table NVARCHAR(511), -- expects schema.table notation
#pk_column SYSNAME, -- column that is primary key
#pk_value INT -- change data type accordingly
AS
BEGIN
SET NOCOUNT ON;
DECLARE #cols NVARCHAR(MAX), #vals NVARCHAR(MAX),
#valOut NVARCHAR(MAX), #valSQL NVARCHAR(MAX);
SELECT #cols = N'', #vals = N'';
SELECT #cols = #cols + ',' + QUOTENAME(name),
#vals = #vals + ' + '','' + ' + 'ISNULL('+REPLICATE(CHAR(39),4)+'+RTRIM(' +
CASE WHEN system_type_id IN (40,41,42,43,58,61) -- datetime types
THEN
'CONVERT(CHAR(8), ' + QUOTENAME(name) + ', 112) + '' ''+ CONVERT(CHAR(14), ' + QUOTENAME(name) + ', 14)'
WHEN system_type_id IN (35) -- text type NOTE: can overflow
THEN
'REPLACE(CAST(' + QUOTENAME(name) + 'as nvarchar(MAX)),'+REPLICATE(CHAR(39),4)+','+REPLICATE(CHAR(39),6)+')'
ELSE
'REPLACE(' + QUOTENAME(name) + ','+REPLICATE(CHAR(39),4)+','+REPLICATE(CHAR(39),6)+')'
END
+ ')+' + REPLICATE(CHAR(39),4) + ',''null'') + '
FROM sys.columns WHERE [object_id] = OBJECT_ID(#table)
AND system_type_id <> 189 -- can't insert rowversion
AND is_computed = 0; -- can't insert computed columns
SELECT #cols = STUFF(#cols, 1, 1, ''),
#vals = REPLICATE(CHAR(39),2) + STUFF(#vals, 1, 6, '') + REPLICATE(CHAR(39),2) ;
SELECT #valSQL = N'SELECT #valOut = ' + #vals + ' FROM ' + #table + ' WHERE '
+ QUOTENAME(#pk_column) + ' = ''' + RTRIM(#pk_value) + ''';';
EXEC sp_executesql #valSQL, N'#valOut NVARCHAR(MAX) OUTPUT', #valOut OUTPUT;
SELECT SQL = 'INSERT ' + #table + '(' + #cols + ') SELECT ' + #valOut;
END
I have created a procedure in dynamic SQL which has a select statement and the code looks like:
ALTER PROCEDURE cagroup (
#DataID INT ,
#days INT ,
#GName VARCHAR(50) ,
#T_ID INT ,
#Act BIT ,
#Key VARBINARY(16)
)
AS
BEGIN
DECLARE #SQL NVARCHAR(MAX)
DECLARE #SchemaName SYSNAME
DECLARE #TableName SYSNAME
DECLARE #DatabaseName SYSNAME
DECLARE #BR CHAR(2)
SET #BR = CHAR(13) + CHAR(10)
SELECT #SchemaName = Source_Schema ,
#TableName = Source_Table ,
#DatabaseName = Source_Database
FROM Source
WHERE ID = #DataID
SET #SQL = 'SELECT ' + #GName + ' AS GrName ,' + #BR
+ #T_ID + ' AS To_ID ,' + #BR
+ #DataID + ' AS DataSoID ,' + #BR
+ #Act + ' AS Active ,' + #BR
+ Key + ' AS key' + #BR
+ 'R_ID AS S_R_ID' + #BR
+ 'FROM' + #DatabaseName + '.'
+ #SchemaName + '.'
+ #TableName + ' t' + #BR
+ 'LEFT OUTER JOIN Gro g ON g.GName = '
+ #GName + #BR + 'AND g.Data_ID] =' + #DataID + #BR
+ 't.[I_DATE] > GETDATE() -' + #days + #BR
+ 'g.GName IS NULL
AND ' + #GName + ' IS NOT NULL
AND t.[Act] = 1' + #BR
PRINT (#SQL)
END
When I am executing this procedure with this statement:
Exec dbo.cagroup 1,10,'[Gro]',1,1,NULL
I am getting the following error.
Msg 245, Level 16, State 1, Procedurecagroup, Line 33
Conversion failed when converting the nvarchar value 'SELECT [Gro] AS GName ,
' to data type int.
Where am I doing wrong?
You need to CAST all numbers to nvarchar in the concatenation.
There is no implicit VBA style conversion to string. In SQL Server data type precedence means ints are higher then nvarchar: so the whole string is trying to be CAST to int.
SET #SQL = 'SELECT ' + #GName + ' AS GrName ,' + #BR
+ CAST(#T_ID AS nvarchar(10)) + ' AS To_ID ,' ...
Edit: Will A has a good point: watch for NULLs!
If you have to build this kind of dynamic SQL, it is better to get the column information from the meta-data than to pass it around.
Select * from Information_Schema.Columns Where Table_name=#TableName
The you have to write an ugly cursor to build the SQL. Expect performance problems. I do lots of this during development to write code for me, but I don't dare run it in production.