How to insert without specifying the column name? - sql

Having many tables (for example two):
|PEOPLE_ID|PEOPLE_NAME|PEOPLE_ACCOUNT|OTHER_NULLABLE_COLUMNS|
|001 |"Andrea" |NULL |OTHER_NULLABLE_COLUMNS|
|002 |"..." |NULL |OTHER_NULLABLE_COLUMNS|
and:
|PET_ID|PET_NAME|PET_OWNER|OTHER_NULLABLE_COLUMNS|
|001 |"Fido" |NULL |OTHER_NULLABLE_COLUMNS|
|002 |"..." |NULL |OTHER_NULLABLE_COLUMNS|
And then I need a generic stored procedure to insert to any table passing the name of the table (pet, people) and the id and name as parameters.
exec sp_inserttottable 'people', '456', 'Gustavo'
exec sp_inserttottable 'pet', '852', 'Scooby'
But in the definition of the Stored Procedure I can't do this:
CREATE PROCEDURE sp_inserttottable
#tableName nvarchar(50),
#codeToInsert nvarchar(4),
#detailToInsert nvarchar(100)
AS
DECLARE #cmd AS NVARCHAR(max)
SET #cmd = 'INSERT INTO ' + #tableName + ' VALUES(' + #codeToInsert + ',' + #detailToInsert + ' )' + ' '
EXEC sp_executesql #cmd
It throw me an error I need to specify the column names like this:
'INSERT INTO ' + #tableNames + ' (PEOPLE_ID, PEOPLE_NAME) VALUES(' + #codeToInsert + ',' + #detailToInsert + ' )' + ' '
But if I do that the procedure can't be generic.
If I can do something like this, put values by column order:
'INSERT INTO ' + #tableNames + ' (1, 2) VALUES(' + #codeToInsert + ',' + #detailToInsert + ' )' + ' '
How to achieve this?
(UPDATE) Applying the Solution of #Gordon Linoff
First using DEFAULT KEYWORD
CREATE PROCEDURE sp_InsertTableType
#tableNames nvarchar(50),
#codeToInsert nvarchar(4),
#detailToInsert nvarchar(100)
AS
DECLARE #cmd AS NVARCHAR(max)
SET #cmd = 'INSERT INTO ' + #tableNames + ' VALUES(' + #codeToInsert + ',' + #detailToInsert + ', DEFAULT)' + ' ';
--SET #cmd = 'INSERT INTO TTCODI_NACI_UPDATE(CODE, DESCRI) VALUES(' + #codeToInsert + ',' + #detailToInsert + ')' + ' '
EXEC sp_executesql #cmd
Then when I call:
exec sp_InsertTableType 'TTCODI_NACI', '1000', 'kuwaiti'
It throws me: Invalid column name '%.*ls'.
And the second Alternative:
Creating the view:
create view TTCODI_NACI_UPDATE
as
SELECT CO_NACI AS CODE, DE_NACI AS DESCRI
, DE_ABRE_NACI AS ABRE
FROM TTCODI_NACI;
Changing the Stored Procedure:
CREATE PROCEDURE sp_InsertTableType
#tableNames nvarchar(50),
#codeToInsert nvarchar(4),
#detailToInsert nvarchar(100)
AS
DECLARE #cmd AS NVARCHAR(max)
--SET #cmd = 'INSERT INTO ' + #tableNames + ' VALUES(' + #codeToInsert + ',' + #detailToInsert + ', DEFAULT)' + ' ';
SET #cmd = 'INSERT INTO TTCODI_NACI_UPDATE(CODE, DESCRI) VALUES('
+ #codeToInsert + ',' + #detailToInsert + ')' + ' '
EXEC sp_executesql #cmd
When I call:
exec sp_InsertTableType 'TTCODI_NACI', '1000', 'kuwaiti'
It throws me: Update or insert of view or function '%.*ls' failed because it contains a derived or constant field.

This is a solution to your problem, but I don't really advocate it. Your tables are for different entities, so I'm not so sure that a generic stored procedure is a good idea.
One solution is to use the DEFAULT keyword:
SET #cmd = 'INSERT INTO ' + #tableName + ' VALUES(DEFAULT, ' + #codeToInsert + ',' + #detailToInsert + ' )' + ' ';
Oh, I really don't like that. It means that the insert depends on the order that the columns are defined in the table -- and woe to anyone who adds a new column and messes up this code, far away from the creation of the new column.
Another solution that I'd be slightly more comfortable with is to create views such as:
create v_people_for_update as
select people_id as id, people_name as name, people_account as account
from people;
Then, insert into the view:
SET #cmd = 'INSERT INTO ' + #viewName(name, account) + ' VALUES(, ' + #codeToInsert + ',' + #detailToInsert + ' )' + ' ';
This at least lets you specify the columns (a good thing) and the collection of views can be named so it is obvious that this stored procedure is working on them.
However, if you have tables with such similar structures, then you should probably combine them into one table and dispense with the idea of using dynamic SQL to choose among different tables. Just use one table and add a column specifying the type of thing that each row refers to.

Related

Passing special character [char(11), char(7)] in stored procedure as string

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.

Compare single values in SQL to identify candidate keys [duplicate]

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

Modify columns using stored procedure in SQL Server

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

Generating Scripts for Specific Records in SQL Server

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

Table Columns as parameters to Stored Procedure [duplicate]

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.