Update column by looping through rows - sql

I have four columns in a table ID, Longitude, Latitude, and SpatialData. I have the first three columns filled out for every row, but I need to enter in the SpatialData for each row. I can currently manually update the SpatialData column by using the below query:
update GioMap set SpatialData = 'Point(-74.009506 40.70602)' Where ID =1
From here I have to keep manually updating the Longitude, Latitude and ID for every row. I am using this code to try to loop through all of the rows and update the table that way:
DECLARE #LoopC INT = 1, #MaxOID INT,
#Long nVarchar(32), #Lat nVarchar(32),#Col1 nVarchar(11)
SET #MaxOID = (select count(*) from GioMap)
Set #Col1 = 'SpatialData'
WHILE(#LoopC <= #MaxOID)
BEGIN
SET #Long = (Select Longitude FROM GioMap where ID = #LoopC)
SET #Lat = (Select Latitude FROM GioMap where ID = #LoopC)
DECLARE #sql VARCHAR(MAX) = ('update GioMap set ' + #Col1 +' = ' + '''' + 'Point(' + #Long + ' ' + #Lat + ')' + '''' + ' Where ID = ' + #LoopC)
EXEC sp_executesql #sql
SET #LoopC = #LoopC + 1
END
When I run this code I keep getting this error message:
Msg 245, Level 16, State 1, Line 13
Conversion failed when converting the nvarchar value 'update [ISSHXI1].[dbo].[GioMap] set SpatialDat = 'Point(-74.0095 40.706)' Where ID = ' to data type int.
I don't understand why it would be trying to convert it to an int?

You could do something like this:
UPDATE GioMap SET SpatialData = 'Point(' + cast(Longitude as varchar) + ' ' + cast(Latitude as varchar) + ')'

I think the way you are doing it is bad, but that's not technically what you asked.
It is trying to convert it to an int because you are adding a varchar to an int. You need to change this:
DECLARE #sql VARCHAR(MAX) = ('update GioMap set ' + #Col1 +' = ' +
'''' + 'Point(' + #Long + ' ' + #Lat + ')' + '''' + ' Where ID = ' +
#LoopC)
to this
DECLARE #sql VARCHAR(MAX) = ('update GioMap set ' + #Col1 +' = ' +
'''' + 'Point(' + #Long + ' ' + #Lat + ')' + '''' + ' Where ID = ' +
Cast(#LoopC as varchar))

The point statement paramaters need to be seperated by a comma.
DECLARE #sql VARCHAR(MAX) = ('update GioMap set ' + #Col1 +' = ' + '''' + 'Point(' + #Long + ' ' + #Lat + ')' + '''' + ' Where ID = ' + #LoopC)
Instead of:
#Long + ' ' + #Lat + ')
try
#Long + ',' + #Lat + ')
To see what is being executed you can try adding a print statement:
DECLARE #sql VARCHAR(MAX) = ('update GioMap set ' + #Col1 +' = ' + '''' + 'Point(' + #Long + ' ' + #Lat + ')' + '''' + ' Where ID = ' + #LoopC)
print #sql
EXEC sp_executesql #sql
Also why do you parans around strings you are assigning? Its confusing in TSQL. While it works it is jarring and unusual.
Instead of:
SET #MaxOID = (select count(*) from GioMap)
try
SET #MaxOID = 'select count(*) from GioMap'
Later in the code you do both parens and quotes. The great, great majority of TSQL developers just use single quotes.
Ben

Related

Dynamic SQL - Insert a variables via INSERT INTO x SELECT statement

I would like to ask how can I insert a variables into a table using INSERT INTO x SELECT statement via dynamic SQL.
I have following table:
|-------------------|-----------------|--------------|-----------------|
| TableName | ColName | Value | SQL_Statement |
|-------------------|-----------------|--------------|-----------------|
I get a content for Value column by this query:
INSERT INTO #ReturnTable(Value) SELECT TreeHolder FROM prm.Schm_Root WHERE ParentTreeHolderId = 'DD040D31-4591-4658-A02E-A6ED00AB64F2';
But I need to fill whole table. Please consider that other values are variables, not SQL queries.
SELECT #TableSchema = TableSchema FROM #TableNames WHERE Id = #Counter;
SELECT #TableName = TableName FROM #TableNames WHERE Id = #Counter;
SELECT #ColName = ColName FROM #TableNames WHERE Id = #Counter;
SET #SQL_Statement = 'SELECT ' + #ColName + ' FROM ' + #TableSchema + '.' + #TableName + ' WHERE ' + #ColName + ' = ' + '''''' + CAST(#GuidArgument AS NVARCHAR(50)) + '''''' + ';';
Now I have this query that fills a table:
SET #SQL_String = N'INSERT INTO #ReturnTable SELECT
''' + #TableName + ''',
''' + #ColName + ''',
''' + #SQL_Statement + ''',
'' + Value + '',
(SELECT ' +
#ColName + '
FROM ' +
#TableSchema + '.' + #TableName + '
WHERE ' +
#ColName + ' = ''' + CAST(#GuidArgument AS NVARCHAR(50)) + '
'')';
EXECUTE sp_executesql #SQL_String
PRINT #SQL_String;
The thing I need is to rewrite this query from INSERT INTO ? VALUE to INSERT INTO ? SELECT format.
If I understand correctly, you want to insert SQL execute syntax string and it results in ReturnTable table.
I would let subquery SQL execute syntax save in a variable. because the will be more clear what you need to do.
Declare a new variable #SQL_excuteStatement variable to save your execute syntax.
the #SQL_Statement to carry the original SQL string.
set #SQL_Statement = 'SELECT ' + #ColName +
' FROM ' + #TableSchema + '.' + #TableName +
' WHERE ' + #ColName + ' = '+'''''' + CAST(#GuidArgument AS NVARCHAR(50)) + '''''';
and use select ... from table instead of subquery in select
There is a sample for you.
DECLARE #SQL_String NVARCHAR(MAX)
DECLARE #TableSchema NVARCHAR(MAX)
DECLARE #TableName NVARCHAR(MAX)
DECLARE #ColName NVARCHAR(MAX)
DECLARE #Counter int = 1
DECLARE #SQL_Statement NVARCHAR(MAX)
DECLARE #GuidArgument INT = 1
CREATE TABLE TableNames(
ID INT,
TableSchema NVARCHAR(100),
TableName NVARCHAR(100),
ColName NVARCHAR(100)
);
CREATE TABLE ReturnTable(
TableName NVARCHAR(100),
ColName NVARCHAR(100),
SQL_Statement NVARCHAR(max),
value nvarchar(max)
);
INSERT INTO TableNames VALUES (1,'dbo','T','val');
CREATE TABLE T(val INT);
INSERT INTO T VALUES (1)
SELECT #TableSchema = TableSchema FROM TableNames WHERE Id = #Counter;
SELECT #TableName = TableName FROM TableNames WHERE Id = #Counter;
SELECT #ColName = ColName FROM TableNames WHERE Id = #Counter;
set #SQL_Statement = 'SELECT ' + #ColName +
' FROM ' + #TableSchema + '.' + #TableName +
' WHERE ' + #ColName + ' = '+ '''''' + CAST(#GuidArgument AS NVARCHAR(50)) + '''''';
SET #SQL_String = N'INSERT INTO ReturnTable (TableName,ColName,SQL_Statement,value)
SELECT '''+ #TableName + ''','''+ #ColName + ''','''+ #SQL_Statement + '''' + ',' + QUOTENAME(#ColName) +
' FROM ' + QUOTENAME(#TableSchema) + '.' + QUOTENAME(#TableName) + '' +
' WHERE ' + #ColName + ' = ''' + CAST(#GuidArgument AS NVARCHAR(50)) + '''';
EXECUTE sp_executesql #SQL_String
sqlfiddle
Note
I would suggest you use clear column after insert into
INSERT INTO table2 (column1, column2, column3, ...)
SELECT column1, column2, column3, ...
FROM table1
WHERE condition;

Comparing string with an UNIQUEIDENTIFIER?

I have written the following procedure:
ALTER PROCEDURE [dbo].[GetLocationOfGuidPre]
#GuidArgument UNIQUEIDENTIFIER
.
.
.
SET #SQL_String = 'INSERT INTO #Guids(FoundGuid) SELECT ' + #ColName + ' FROM ' + #TableSchema + '.' + #TableName + ' WHERE ' + #ColName + ' = ' + #GuidArgument;
When I try to execute it, I get this error:
The data types nvarchar and uniqueidentifier are incompatible in the add operator.
How can I compare a string value with UNIQUEIDENTIFIER?
Have you tried passing it as a parameter?
SET #SQL_String = 'INSERT INTO #Guids(FoundGuid) SELECT ' + #ColName + ' FROM ' + #TableSchema + '.' + #TableName + ' WHERE ' + #ColName + ' = #GuidArgument';
EXEC sp_executesql #SQL_string,
N'#GuidArgument UNIQUEIDENTIFIER',
#GuidArgument = #GuidArgument;
I guess the reason for this error is pretty clear: it says + ' = ' + #GuidArgument; does not work, since you try to add an unique ID to a varchar... just try to cast your #GuidArgumentas varchar and it should work.

Adding an apostrophe into a dynamic SQL

I would like to ask how can I add an apostrophe into a dynamic SQL. I need to return an SQL statement in one of the columns which has to have apostrophes in itself.
I have the following statement:
SET #SQL_String = N'INSERT INTO #ReturnTable
(
TableName,
ColName,
SQL_Statement,
Value
)
VALUES
(
''' + #TableName + ''',
''' + #ColName + ''',
''' +
'SELECT ' +
#ColName +
' FROM ' +
#TableSchema + '.' + #TableName +
' WHERE ' +
#ColName + ' = ' + CAST(#GuidArgument AS NVARCHAR(50)) + ';' +''',
(
SELECT
' + #ColName + '
FROM
' + #TableSchema + '.' + #TableName +
' WHERE '
+ #ColName + ' = ''' + CAST(#GuidArgument AS NVARCHAR(50)) +
'''))';
Executing with:
EXECUTE #RC = [dbo].[GetLocationOfGuidPre] 'F2CAB996-F00F-43B8-A67A-0000721A829D'
I need to put a whole first CAST into a pair of '.
I've tried:
Putting whole CAST statement into a separeted variable like: DECLARE #Test NVARCHAR(50);
SET #Test = CAST(#GuidArgument AS NVARCHAR(50));
SET #Test = 'CAST(#GuidArgument AS NVARCHAR(50))';
SET #Test = '''CAST(#GuidArgument AS NVARCHAR(50))''';
Addidng two more apostrophes:
' WHERE ' + #ColName + ' = ''' + CAST(#GuidArgument AS NVARCHAR(50)) + ''';'
Please use CHAR(39) instead of typing ' in your dynamic code directly.
Example:
declare #my_dynamic_sql nvarchar(max) = 'print char(39);';
exec(#my_dynamic_sql);

Population of Visual Studio Database Project data-loading scripts from existing data

So, I've been hunting for a solution for this for awhile and have come up with what sort of works ... but I can't help feeling that there must be something more elegant.
What I'm looking for is to be able to extract existing data from a populated database & incorporate that data into loading scripts. The database schema & configuration data will be rolled out multiple times, and will change as development continues, so it's important to be able to rebuild the configuration data from existing data, rather than from static data kept in scripts.
Here's what I've cobbled together:
create procedure #dump (
#TableName varchar(128)
)
as
set nocount on
set rowcount 0
declare #template varchar(max)
set #template = 'SET IDENTITY_INSERT [dbo].[' + #TableName + '] ON
MERGE INTO [dbo].[' + #TableName + '] AS [Target]
USING
(
VALUES
{vals}
)
AS [Source] ({fields})
ON [Target].[{pk}] = [Source].[{pk}]
WHEN MATCHED THEN UPDATE SET
{upds}
WHEN NOT MATCHED BY TARGET THEN
INSERT
(
{fields}
)
VALUES
(
{fields}
);
SET IDENTITY_INSERT [dbo].[' + #TableName + '] OFF
--------------------------------------------------
'
declare #pk varchar(max) = ''
declare #vals varchar(max) = '/*
set concat_null_yields_null off
select ''' + '(' + ''' + replace(replace({casts} + '') ,'', '',,'', '', null,''), ''' + ',)' + ''', ''' + ',null)' + ''') from [' + #TableName + ']
*/'
declare #casts varchar(max) = ''
declare #fields varchar(max) = ''
declare #upds varchar(max) = ''
declare #inserts varchar(max) = ''
set #pk = SUBSTRING(#TableName, 1, len(#TableName) - 1) + 'ID'
declare cur_flds
cursor for select c.name, c.type
from syscolumns c
where c.id = object_id(#TableName)
order by c.colid
declare #fn varchar(max)
declare #ft int
open cur_flds
fetch next from cur_flds into #fn, #ft
while ##FETCH_STATUS = 0
begin
if len(#fields) > 0
set #fields = #fields + ', '
set #fields = #fields + '[' + #fn + ']'
if len(#casts) > 0
set #casts = #casts + ' + ' + ''','' + '
if #ft in(56,55,50,38,48)
set #casts = #casts + 'cast([' + #fn + '] as varchar)'
else if #ft = 111
set #casts = #casts + ''''''''' + ' + 'cast([' + #fn + '] as varchar) + ' + ''''''''''
else
set #casts = #casts + ''''''''' + ' + 'replace([' + #fn + '], ''''''''' + ', ' + ''''''''''''') + '''''''''
if #fn != #pk
begin
if len(#upds) > 0
set #upds = #upds + ', '
set #upds = #upds + '[Target].[' + #fn + '] = [Source].[' + #fn + ']'
end
fetch next from cur_flds into #fn, #ft
end
close cur_flds
deallocate cur_flds
set #vals = REPLACE(#vals, '{casts}', #casts)
set #template = REPLACE(#template, '{pk}', #pk)
set #template = REPLACE(#template, '{vals}', #vals)
set #template = REPLACE(#template, '{fields}', #fields)
set #template = REPLACE(#template, '{upds}', #upds)
set #template = REPLACE(#template, '{inserts}', #inserts)
print #template
go
exec #dump 'ActionItemSystems'
drop proc #dump
That ends up giving me output of:
SET IDENTITY_INSERT [dbo].[ActionItemSystems] ON
MERGE INTO [dbo].[ActionItemSystems] AS [Target]
USING
(
VALUES
/*
set concat_null_yields_null off
select '(' + replace(replace(cast([ActionItemSystemID] as varchar) + ',' + '''' + replace([ActionItemSystemName], '''', '''''') + '''' + ') ,', ',,', ', null,'), ',)', ',null)') from [ActionItemSystems]
*/
)
AS [Source] ([ActionItemSystemID], [ActionItemSystemName])
ON [Target].[ActionItemSystemID] = [Source].[ActionItemSystemID]
WHEN MATCHED THEN UPDATE SET
[Target].[ActionItemSystemName] = [Source].[ActionItemSystemName]
WHEN NOT MATCHED BY TARGET THEN
INSERT
(
[ActionItemSystemID], [ActionItemSystemName]
)
VALUES
(
[ActionItemSystemID], [ActionItemSystemName]
);
SET IDENTITY_INSERT [dbo].[ActionItemSystems] OFF
From this point, I can take the commented-out bit
set concat_null_yields_null off
select '(' + replace(replace(cast([ActionItemSystemID] as varchar) + ',' + '''' + replace([ActionItemSystemName], '''', '''''') + '''' + ') ,', ',,', ', null,'), ',)', ',null)') from [ActionItemSystems]
execute that, and get output like:
(33,'7111-5 -Upstream/Seed Lab') ,
(32,'7301-Seed Lab') ,
(30,'7807 UFDF') ,
(14,'BAS Panel Upgrade') ,
(1,'Clean Steam') ,
(13,'DCS') ,
(2,'HWFI') ,
(3,'MCS') ,
(12,'MES') ,
(31,'Seed Lab') ,
(18,'UCS WRO') ,
(34,'Upstream Seed Lab') ,
(29,'Viral Filtration') ,
which can then be incorporated (sans the final comma) into the script.
Now, this solution functions, but it's fragile. It depends on various assumptions (e.g., that the Table Name will have a Primary Key of Table Name - trailing 's' and plus ID) that may not hold true for every solution. It also requires cutting & pasting, and rerunning from the start when the table structures change.
This is probably quite a lot of background ... which I'm partly sharing because I couldn't find anything similar out there & thought that somebody might benefit from this. However, I still come back to my real question, which is to say: where's the tool to generate this kind of script for VS Database Projects? There really should be something - something that would take into account whatever the primary key is, that would generate the thing entire, etc.
You can try with this procedure for generating MERGE statements:
https://github.com/readyroll/generate-sql-merge
It is more advanced version of what you already have.

How do I script my table to generate INSERT INTO commands?

I have a lookup table with about 10 records, I know I can script the structure to a text file, but how can I script the data to insert into commands?
Ten records, and it's urgent?
Just type it out manually. Should be pretty easy to cut-n-paste.
Assuming SQL Server...
SQL Management Studio will generate an insert script. Right-click your database and select Tasks-Export data
This depends pretty much on the tools you are using...
The quick and dirty way is to run a select into a string and tell sql enterprise manager to give you text (not grid) as the output
SELECT 'INSERT INTO TABLES (fields here) VALUES (' + field1 + ', '....
Do something like this:
select "insert into my_targ_table(my_field_1, my_field_2, ..., my_field_n) values(" || x.my_field_1_col || ", " || x.my_field_2_col || ");"
from my_source_table x
Then just run the script you've generated.
This code works with all tables
DECLARE #TblName varchar(128)
DECLARE #WhereClause varchar(255)
DECLARE #cmd1 varchar(7000)
DECLARE #cmd2 varchar(7000)
SET #TblName = '<tablename>' --Name of your table
SET #WhereClause = ' ' --where clause ex columnA = 1
SET #cmd1 = 'SELECT '' INSERT INTO ' + #TblName + ' ( '
SET #cmd2 = ' + '' VALUES ( '' + '
create table #tableDef (id int identity (1,1), ColType int, ColName varchar(128))
--Fetch column names and datatypes
insert #tableDef (ColType, ColName)
select case when DATA_TYPE like '%char%' then 1
when DATA_TYPE like '%datetime%' then 2
else 0 end ,
COLUMN_NAME
from information_schema.columns
where TABLE_NAME = #TblName
order by ORDINAL_POSITION
SELECT #cmd1 = #cmd1 + ColName + ',',
#cmd2 = #cmd2
+ ' CASE WHEN ' + ColName + ' IS NULL '
+ ' THEN ''NULL'' '
+ ' ELSE '
+ case ColType
when 1 then ''''''''' + ' + ColName + ' + '''''''''
when 2 then ''''''''' + ' + 'CONVERT(VARCHAR(20),' + ColName + ')' + ' + '''''''''
else 'CONVERT(VARCHAR(20),' + ColName + ')' end
+ ' END + '','' + '
from #tableDef
order by id
select #cmd1 = left(#cmd1,len(#cmd1)-1) + ' ) '' '
select #cmd2 = left(#cmd2,len(#cmd2)-8) + '+'')'' FROM ' + #tblName + #WhereClause
select '/*' + #cmd1 + #cmd2 + '*/'
exec (#cmd1 + #cmd2)
drop table #tableDef