Update multiple rows by values taken from another table - tsql

Here is what I could master so far:
declare #i int = 7;
declare #lat float;
declare #lng float;
declare #point ???; -- what type?
declare #location table(lat float, lng float)
insert #location values (32.097218, 34.770438199999944)
insert #location values (32.083847, 34.775618)
insert #location values (32.1600788, 34.771902)
insert #location values (31.9914283, 34.80780099999993)
insert #location values (32.1574281, 34.775191800000016)
insert #location values (29.5543212, 34.89448429999993)
insert #location values (32.8018919, 34.96268420000001)
while #i > 0
begin
-- this is a wrong way to do it
set #point = (select top (1) * from #location);
-- must set #lat and #lng here somehow, based on the
-- one row selected above. Deleting here is not
-- mandatory, but may be used, if needed.
delete top (1) from #location;
update top (1) rest_tbl
set lat = #lat, lng = #lng
where lat is null and private_label_id = 3
set #i = #i - 1
end;
Please don't mind the part, where I'm creating the #location variable - in the real world, it is an actual table, I'm just using it for PoC.

I think this is what you need. If you are on sql-server 2005 or above you could use CTE and row_number() function as below without a loop. Please replace cols_that_decides_order with correct column(s) to get the top records.
Also I think your lng will be rounded up when converting to float (ex; 34.770438199999944 >> 34.7704382).
--Declare the table with auto incremented identity with seed=7 and increment = -1.
declare #location table(mykey int identity(7,-1), lat float, lng float)
insert #location values (32.097218, 34.770438199999944)
,(32.083847, 34.775618)
,(32.1600788, 34.771902)
,(31.9914283, 34.80780099999993)
,(32.1574281, 34.775191800000016)
,(29.5543212, 34.89448429999993)
,(32.8018919, 34.96268420000001)
;with cte as (
select lat,lng, row_number() over (order by cols_that_decides_order) rn
from rest_tbl
where lat is null and private_label_id = 3
)
update c set c.lat = t.lat, c.lng = t.lng
from cte c join #location t on c.rn = t.myKey

I think Using Cursor in this matter will help
DECLARE db_cursor CURSOR FOR (Select lat,lng from #location
where lat not in (Select lat from rest_tbl where lat is not null));
DECLARE #lat Float;
DECLARE #lng Float;
OPEN db_cursor;
FETCH NEXT FROM db_cursor INTO #lat,#lng;
WHILE ##FETCH_STATUS = 0
BEGIN
UPDATE top (1) rest_tbl
SET lat =#lat,lng =#lng
where lat is null
FETCH NEXT FROM db_cursor INTO #lat,#lng;
END;
CLOSE db_cursor;
Delete from #location
where lat in (Select lat from rest_tbl where lat is not null)

Related

SQL Server 2012: check if is uniqueidentifier and convert to string

I am trying to test if a variable is a uniqueidentifier and if it is then convert it to string but it fails:
Declare #Item VARCHAR(50)
Declare #OutString varchar(max) ;
--#Outstring is populated from various tables each cell separated by ','
--Getting the #Item from the #Outstring and convert it if its uid
DECLARE #Pos INT
DECLARE #Loop BIT
SELECT #Loop = CASE WHEN LEN(#OutString) > 0 THEN 1 ELSE 0 END
WHILE (SELECT #Loop) = 1
BEGIN
SELECT #Pos = CHARINDEX(',', #OutString, 1)
IF #Pos > 0
BEGIN
SELECT #Item = SUBSTRING(#OutString, 1, #Pos - 1)
SELECT #OutString = SUBSTRING(#OutString, #Pos + 1, LEN(#OutString) - #Pos)
IF (TRY_CONVERT(UNIQUEIDENTIFIER, #Item) IS NOT NULL)
BEGIN
CONVERT(NVARCHAR(50), #Item) AS #Item --ERROR LINE incorrect syntax
END
END
END
it is either
select #Item = convert(nvarchar(50), #Item)
or
select #Item = cast(#Item as nvarchar(50))
The syntax error is because you have said the action but not what SQL should do with it.
Do you want the string to be returned?
SELECT CONVERT(NVARCHAR(50), #Item) AS Item
Appended to #Item? (but this variable is inside your loop?)
SET #Item += CONVERT(NVARCHAR(50), #Item)
Not sure what you want to do once you have converted the string. Maybe you need another variable to add the string on (like above except not SET #Item)
Use
SELECT convert(nvarchar(50), #Item ) as Item

Conversion failed when converting date and/or time from character string - SQL Server 2008 R2

I am busy with a stored procedure to calculate production numbers of shifts. I already have an idea on how to do that but for some kind of strange reason I do not get an insert into with a variable time working. Below is query for the stored procedure:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[ProductionReport]
#filterStartTime datetime,
#filterEndTime datetime,
#machine varchar(10)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
CREATE TABLE #tempProductionTable(
id varchar(3),
ploeg varchar(3),
starttime2 datetime,
endtime2 datetime,
daynumber int)
declare #i int
, #SQLString varchar(400)
, #id varchar(3)
, #ploeg varchar(3)
, #starttime datetime
, #endtime datetime
set #i = 0
while #i < 16
begin
set #i = #i+1
set #id = #i
set #starttime = convert(datetime, #filterStartTime,110)
print #starttime
set #ploeg = '2'
SET #SQLString = 'INSERT INTO #tempProductionTable (id,ploeg,starttime2) values ('+#id+','+#ploeg+','+#starttime+')'
EXEC(#SQLString)
end
-- Insert statements for procedure here
SELECT * from #tempProductionTable
END
And this is the query for opening the stored procedure:
USE [NRPConfiguration]
GO
DECLARE #return_value int
EXEC #return_value = [dbo].[ProductionReport]
#filterStartTime = '2017-01-01 10:00:00.000',
#filterEndTime = N'2-1-2017 0:00',
#machine = N'ASM_008'
SELECT 'Return Value' = #return_value
GO
I already tried a lot of things but still can't get it working. For example when I manually insert a time than it is working. But when I want to do it with an variable it is not working also when I am using the convert function for it. What am I doing wrong?
I use SQL Server 2008 R2 for this.
Quit concatenating strings to executed dynamic sql, use sp_executesql instead.
Your error can be correct by specifying dates in ISO format (or in the format Dan Bracuk mentioned in his comment). e.g. '2017-04-01T23:59:59.363'
#BackToBasics : Dating Responsibly - Aaron Bertrand
Here is how you would use sp_executesql instead:
alter procedure [dbo].[ProductionReport] (
#filterStartTime datetime,
#filterEndTime datetime,
#machine varchar(10)
) as
begin;
-- set nocount on added to prevent extra result sets from
-- interfering with select statements.
set nocount on;
create table #tempProductionTable(
id varchar(3)
, ploeg varchar(3)
, starttime2 datetime
, endtime2 datetime
, daynumber int
);
declare #i int
, #params nvarchar(max)
, #sqlstring nvarchar(max)
, #id varchar(3)
, #ploeg varchar(3)
, #starttime datetime
, #endtime datetime;
set #params = '#id int, #ploeg varchar(3), #starttime datetime';
set #sqlstring = 'insert into #tempProductionTable (id,ploeg,starttime2) values (#id,#ploeg,#starttime);';
set #i = 0
while #i < 16
begin
set #i = #i+1
set #id = #i
set #starttime = convert(datetime, #filterStartTime,110)
set #ploeg = '2'
print #starttime
exec sp_executesql #sqlstring, #params, #id, #ploeg, #starttime;
end
-- Insert statements for procedure here
select * from #tempProductionTable
end;
go
rextester demo: http://rextester.com/KPBR1056

How does one use loops in TSQL?

In TSQL, I would like to change the following code from have to use hard coded dhomes to using a loop for optimization. My failed attempt at trying to add a loop is also included.
Declare #dhome Tinyint, #bp smallint, #lr smallint, #q smallint
// Set #dhome = 1
While(#dhome <= 3) // My attempt to add a loop
SELECT #lr = MAX(NQdDate), #q = NQd
FROM NQdHistory
WHERE dhomeId = #dhome
GROUP BY NQdDate, NQd
SELECT #bd = COUNT(*)
FROM bdhome
WHERE NQdDate= #lr AND dhomeID= #dhome
DELETE FROM ND1 WITH(XLOCK)
WHERE dhomeID= #dhome AND NQdDate= #lr
UPDATE NQdHistory
SET Nbd = #q - ##RowCount - #bp, NBd = #bp
WHERE NQdDate= #lr AND dhomeID= #dhome
Set #dhome = #dhome +1 //My attempt to end a loop
You're on the right track. You're missing your begin and end. Also, be sure to give #dhome a value. It looks like you started to and have it commented out on your third line:
Declare #dhome Tinyint, #bp smallint, #lr smallint, #q smallint
// Set #dhome = 1
While(#dhome <= 3) // My attempt to add a loop
begin
SELECT #lr = MAX(NQdDate), #q = NQd
FROM NQdHistory
WHERE dhomeId = #dhome
GROUP BY NQdDate, NQd
SELECT #bd = COUNT(*)
FROM bdhome
WHERE NQdDate= #lr AND dhomeID= #dhome
DELETE FROM ND1 WITH(XLOCK)
WHERE dhomeID= #dhome AND NQdDate= #lr
UPDATE NQdHistory
SET Nbd = #q - ##RowCount - #bp, NBd = #bp
WHERE NQdDate= #lr AND dhomeID= #dhome
Set #dhome = #dhome +1 //My attempt to end a loop
end
If you're familiar with C/C#/C++, think of T-SQL's Begin and End like curly braces { and }, if you're more familiar with VB Then and End If. Or more like pascals Begin and End. You get the idea :)
Missing a begin and end on your while.
WHILE (Transact-SQL)
Example 1
DECLARE #I INT,#COUNTVAR INT
SET #I = 1
DECLARE #Parent_Child TABLE(ID INT IDENTITY(1,1),ParentPositionID INT NULL,ChildPositionId Int)
INSERT INTO #Parent_Child(ParentPositionID,ChildPositionId)
SELECT DISTINCT PARENT_POSITION_ID,CHILD_POSITION_ID from tblPOSITION_HIERARCHY
--WHERE CHILD_POSITION_ID IN (--YOUR CONDITION IF ANY)
SELECT #COUNTVAR =COUNT(*) FROM #PTS_Parent_Child
DECLARE #int_SUPE_POSITION_ID INT, #int_CHILD_POSITION_ID INT
--loop through records here
WHILE #I <= #COUNTVAR
BEGIN
SELECT #int_SUPE_POSITION_ID=ParentPositionID,#int_CHILD_POSITION_ID=ChildPositionId FROM #Parent_Child WHERE ID=#I
--Whatever you want to do with records
SET #I=#I+1
END
Example 2
Just another approach if you are fine using temp tables.I have personally tested this and it will not cause any exception (even if temp table does not have any data.)
CREATE TABLE #TempTable
(
ROWID int identity(1,1) primary key,
HIERARCHY_ID_TO_UPDATE int,
)
--INSERT DATA INTO TEMP TABLE USING INSERT INTO CLAUSE OR FOR EAXMPLE BELOW
--INSERT INTO #TempTable VALUES(1)
--INSERT INTO #TempTable VALUES(2)
--INSERT INTO #TempTable VALUES(4)
--INSERT INTO #TempTable VALUES(6)
--INSERT INTO ##TempTable VALUES(8)
DECLARE #MAXID INT
SET #COUNTER =1
SELECT #MAXID=COUNT(*) FROM #TempTable
--PRINT #MAXID
WHILE (#MAXID > 0)
BEGIN
--DO THE PROCESSING HERE
SELECT #HIERARCHY_ID_TO_UPDATE =PT.HIERARCHY_ID_TO_UPDATE FROM #TempTable PT WHERE ROWID=#COUNTER
--PRINT '#MAXID VALUE '
--PRINT #MAXID
SET #MAXID=#MAXID-1
SET #COUNTER =#COUNTER + 1
End
If(OBJECT_ID('tempdb..#TempTable') IS NOT NULL)
BEGIN
DROP TABLE #TempTable
END

Add comma every nth character in value

my problem is pretty simple. I get a value from a sql select which looks like this:
ARAMAUBEBABRBGCNDKDEEEFOFIFRGEGRIEISITJPYUCAKZKG
and I need it like this:
AR,AM,AU,BE,BA,BR,BG,CN,DK,DE,EE,FO,FI,FR,GE,GR,IE,IS,IT,JP,YU,CA,KZ,KG
The length is different in each dataset.
I tried it with format(), stuff() and so on but nothing brought me the result I need.
Thanks in advance
With a little help of a numbers table and for xml path.
-- Sample table
declare #T table
(
Value nvarchar(100)
)
-- Sample data
insert into #T values
('ARAMAU'),
('ARAMAUBEBABRBGCNDKDEEEFOFIFRGEGRIEISITJPYUCAKZKG')
declare #Len int
set #Len = 2;
select stuff(T2.X.value('.', 'nvarchar(max)'), 1, 1, '')
from #T as T1
cross apply (select ','+substring(T1.Value, 1+Number*#Len, #Len)
from Numbers
where Number >= 0 and
Number < len(T1.Value) / #Len
order by Number
for xml path(''), type) as T2(X)
Try on SE-Data
Time to update your resume.
create function DontDoThis (
#string varchar(max),
#count int
)
returns varchar(max)
as
begin
declare #result varchar(max) = ''
declare #token varchar(max) = ''
while DATALENGTH(#string) > 0
begin
select #token = left(#string, #count)
select #string = REPLACE(#string, #token, '')
select #result += #token + case when DATALENGTH(#string) = 0 then '' else ',' end
end
return #result
end
Call:
declare #test varchar(max) = 'ARAMAUBEBABRBGCNDKDEEEFOFIFRGEGRIEISITJPYUCAKZKG'
select dbo.DontDoThis(#test, 2)
gbn's comment is exactly right, if not very diplomatic :) TSQL is a poor language for string manipulation, but if you write a CLR function to do this then you will have the best of both worlds: .NET string functions called from pure TSQL.
I believe this is what QQping is looking for.
-- select .dbo.DelineateEachNth('ARAMAUBEBABRBGCNDKDEEEFOFIFRGEGRIEISITJPYUCAKZKG',2,',')
create function DelineateEachNth
(
#str varchar(max), -- Incoming String to parse
#length int, -- Length of desired segment
#delimiter varchar(100) -- Segment delimiter (comma, tab, line-feed, etc)
)
returns varchar(max)
AS
begin
declare #resultString varchar(max) = ''
-- only set delimiter(s) when lenght of string is longer than desired segment
if LEN(#str) > #length
begin
-- continue as long as there is a remaining string to parse
while len(#str) > 0
begin
-- as long as know we still need to create a segment...
if LEN(#str) > #length
begin
-- build result string from leftmost segment length
set #resultString = #resultString + left(#str, #length) + #delimiter
-- continually shorten result string by current segment
set #str = right(#str, len(#str) - #length)
end
-- as soon as the remaining string is segment length or less,
-- just use the remainder and empty the string to close the loop
else
begin
set #resultString = #resultString + #str
set #str = ''
end
end
end
-- if string is less than segment length, just pass it through
else
begin
set #resultString = #str
end
return #resultString
end
With a little help from Regex
select Wow=
(select case when MatchIndex %2 = 0 and MatchIndex!=0 then ',' + match else match end
from dbo.RegExMatches('[^\n]','ARAMAUBEBABRBGCNDKDEEEFOFIFRGEGRIEISITJPYUCAKZKG',1)
for xml path(''))

Assign a list of integers to an #var

I can:
declare #idOrder int
set #idOrder = 21319
I want:
declare #idOrder int
set #idOrder = (21319, 21320)
for use in a series of statements where the 'WHERE' clause uses the IN operator
delete Orders
where idOrder in #idOrder
instead of
delete Orders
where idOrder in (21319, 21320)
You can't do that as long as it's an int, as that's not a valid value for that datatype. A datatype that could take several integers is a table
declare #idOrder table (id int)
insert into #idOrder values(21319)
insert into #idOrder values(21320)
delete from Orders where idOrder in (select id from #idOrder)
In SQL Server you can also
CREATE FUNCTION [dbo].[fn_ado_param_int] (#ado nvarchar(4000))
RETURNS #VALUES TABLE (ado int)AS
BEGIN
declare #Delim char(1)
set #Delim = ','
DECLARE #chrind INT
DECLARE #Piece nvarchar(4000)
SELECT #chrind = 1
WHILE #chrind > 0
BEGIN
SELECT #chrind = CHARINDEX(#Delim,#ado)
IF #chrind > 0
SELECT #Piece = LEFT(#ado,#chrind - 1)
ELSE
SELECT #Piece = #ado
INSERT #VALUES(ado) VALUES(#Piece)
SELECT #ado = RIGHT(#ado,LEN(#ado) - #chrind)
IF LEN(#ado) = 0 BREAK
END
RETURN
END
declare #idOrder varchar(500);
set #inOrder = "21319,2138,2138";
delete from Orders where id in (select ado from dbo.fn_ado_param_int(#idOrder));