Updating SQL Server query loop until X number reached - c#

I am in need of updating a few rows in a database at the same time. Now the issue is that I will only know the FIRST row's unique ID but I will have X number of other rows after that that will need to be updated with some data as well. I will know how many loops I will need in order to update them - just need to know how to go about doing it.
My query looks like this:
UPDATE bLine
SET #val1
WHERE theGuid = #val2;
Now the data for #val1 looks like this:
ID | qty_SerialNum | qty_Location | qty_Property | theGUID
---+---------------+--------------+--------------+---------------------------
5 | 6845fg56 | Home | NA | a45vz-u300-2bd-4710j-vf09
6 | fk469fkh | Dock#4 | NA |
7 | geww2 | Dock#1 | Local |
...
Which when in a string to send over for the #val1:
#val1 = qty_SerialNum = '6845fg56,fk469fkh,geww2',
qty_Location = 'Home,Dock#4,Dock#1',
qty_Property = 'NA,NA,Local'
#val2 = theGUID = 'a45vz-u300-2bd-4710j-vf09'
So that's all fine since I know the GUID of the first row that needs updating but every row after that I wont know.
So the 1st update would look like out of 3:
UPDATE
bLine
SET
(qty_SerialNum = '6845fg56',qty_Location = 'Home',qty_Property = 'NA' )
WHERE
theGuid = 'a45vz-u300-2bd-4710j-vf09';
And this would be the 2nd update out of 3:
UPDATE
bLine
SET
(qty_SerialNum = 'fk469fkh',qty_Location = 'Dock#4',qty_Property = 'NA' )
WHERE
???? = ????;
And finally this would be the 3rd update out of 3:
UPDATE
bLine
SET
(qty_SerialNum = 'geww2',qty_Location = 'Dock#1',qty_Property = 'Local' )
WHERE
???? = ????;
So the question here is - how can I loop through the next X rows and update that information with the 2,3,... values in #val1 (skipping the first since its already saved via the theGUID)?

You can treat this as a gaps and islands type problem where you need to update and island row(row containing guid as well all gap rows till next island).
I am taking some assumption here and below is a working demo
create table bline (ID int, qty_SerialNum varchar(100),qty_Location varchar(100),qty_Property varchar(100), theGUID varchar(100))
insert into bline values
(5,'random','garbage','existing','a45vz-u300-2bd-4710j-vf09')
,(6,'data','random','garbage', NULL)
,(7,'existing','data','.', NULL);
create table #V (qty_SerialNum varchar(100),qty_Location varchar(100),qty_Property varchar(100))
insert into #V values
('6845fg56','Home','NA'),
('fk469fkh','Dock#4','NA'),
('geww2','Dock#1','Local')
;with map as
(
select id,
rn=row_number() over ( order by id asc)
from bline
where theGUID is NOT NULL
),
mappedBline as
(
select
b1.*,
rn
from
bline b1 join
(
select b.id,
rn=max(rn)
from bline b
join map m
on b.id >=m.id
group by b.id
)b2
on b2.id=b1.id
),
updateSet as
(
select
M.*,
updaterow=row_number() over( order by M.id)
from mappedBline M join
mappedBline M2
on M.rn=M2.rn
and M2.theGUID = 'a45vz-u300-2bd-4710j-vf09' --#val2
)
update U
set
qty_SerialNum=V.qty_SerialNum,
qty_Location= V.qty_Location,
qty_Property =V.qty_Property
from
updateSet U join
( select
*, updaterow =row_number() over (order by (select NULL))
from #V
)V
on U.updaterow=V.updaterow
select * from bline

You have a very arcane problem. In general, SQL updates are not for "adjancent" rows in this fashion. However, you can make it work by storing the values in a (virtual) table and joining the two sides together:
with v as (
select v.*
from (values (1, '6845fg56', 'Home', 'NA'),
(2, 'fk469fkh', 'Dock#4', 'NA'),
(3, 'geww2', 'Dock#1', 'Local')
) v(seqnum, qty_SerialNum, qty_Location, qty_Property)
)
update b
set qty_SerialNum = v.qty_SerialNum,
qty_Location = v.qty_Location,
qty_Property = v.qty_Property
from (select top(3) b.*,
row_number() over (order by id) as seqnum
from bline b
where id >= (select id from bline where theGUID = 'a45vz-u300-2bd-4710j-vf09'
order by id
) b join
v
on b.seqnum = v.seqnum;

Related

Alternating Columns of 2 SQL Queries

I have an oracle query that gets data from two different tables, displaying them as:
Project | 20151 | 20152 | 20153 | 20154 | Project_1 | 20151_1 | 20152_1 | 20153_1 | 20154_1
-------------------------------------------------------------------------------------------
This does get the correct data for what I need, however I need a way of alternating the columns from each of the tables. E.g:
Project | Project_1 | 20151 | 20151_1 | ...
---------------------------------------
The query I have is as follows:
SELECT * FROM(
SELECT * FROM
(
SELECT planned.YEAR || planned.MONTH as AAA, planned.DAYS as Amount, PROJ.NAME AS Project
FROM PLANNED_RESOURCE_TAB planned, USER_LINK_VIEW PROJ
WHERE ACTUAL.U_P_LINK = PROJ.ID
AND PROJ.USER_ID = '1'
)
PIVOT
(
SUM(Amount) FOR AAA in (20151,20152,20153,20154)
)
)
PLANNED__ RIGHT OUTER JOIN
(
SELECT * FROM
(
SELECT ACTUAL.YEAR || ACTUAL.MONTH as BBB, ACTUAL.DAYS as Amount, PROJ.NAME AS Project
FROM ACTUAL_RESOURCE_TAB actual, USER_LINK_VIEW PROJ
WHERE ACTUAL.U_P_LINK = PROJ.ID
AND PROJ.USER_ID = '1'
)
PIVOT
(
SUM(Amount) FOR BBB in (20151,20152,20153,20154)
)
)ACTUAL__
ON PLANNED__.PROJECT = ACTUAL__.PROJECT
The data results will be displayed on a ASP.NET page, so if it is best to manipulate the columns on the page itself that is no problem. I mainly want to know whether it is possible to do it with SQL beforehand, and subsequently how I'd go about that.
The columns cannot be declared in the very first select statement, they will vary dependent on a users input. The values that are inside the pivot are just for my testing purposes, they will be generated dynamically from the ASP.NET page.
Thanks in advance!
Have you tried to replace the * with the retrieved columns of the most outer select? Something like this:
SELECT PLANNED__.Project,
ACTUAL__.Project,
PLANNED__."20151",
ACTUAL__."20151",
PLANNED__."20152",
ACTUAL__."20152",
PLANNED__."20153",
ACTUAL__."20153",
PLANNED__."20154",
ACTUAL__."20154"
FROM
(SELECT *
FROM
(SELECT planned.YEAR
|| planned.MONTH AS AAA,
planned.DAYS AS Amount,
PROJ.NAME AS Project
FROM PLANNED_RESOURCE_TAB planned,
USER_LINK_VIEW PROJ
WHERE ACTUAL.U_P_LINK = PROJ.ID
AND PROJ.USER_ID = '1'
) PIVOT ( SUM(Amount) FOR AAA IN (20151,20152,20153,20154) )
) PLANNED__
RIGHT OUTER JOIN
(SELECT *
FROM
(SELECT ACTUAL.YEAR
|| ACTUAL.MONTH AS BBB,
ACTUAL.DAYS AS Amount,
PROJ.NAME AS Project
FROM ACTUAL_RESOURCE_TAB actual,
USER_LINK_VIEW PROJ
WHERE ACTUAL.U_P_LINK = PROJ.ID
AND PROJ.USER_ID = '1'
) PIVOT ( SUM(Amount) FOR BBB IN (20151,20152,20153,20154) )
)ACTUAL__
ON PLANNED__.PROJECT = ACTUAL__.PROJECT

Is there any way to get column value on the basis of row number?

I have a table valued function [dbo].GetValues() in my SQL Server database which is written in C#. This function returns a table of integers inside the text specified in the arguments. For example, [dbo].GetValues('232dasdg34vb3') will return following table:
| Ints |
| ---- |
| 232 |
| 34 |
| 3 |
Now I am trying to use this resultant table in a WHILE loop:
DECLARE #IntCount int = (SELECT COUNT(*) FROM [dbo].GetValues('232dasdg34vb3'))
WHILE(#IntCount > 0)
BEGIN
-- What to do here??
SET #IntCount = #IntCount - 1
END
So, is there any way I can access the rows one by one in this while loop using some index or row number?
Also, please note that I don't have access to the source code of GetValues().
UPDATE (Actual Problem)
There are three tables in database A1, A2 and A3. All of these tables has a column [ID] that is foreign key to other table in following way:
[A1].[ID] is connected to [A2].[A1ID]
[A2].[ID] is connected to [A3].[A2ID]
The text passed as argument to the function contains integers that are the [ID]s of A3 table. Now, I want the rows from A1 and A2 table using the [ID] of A3 table.
I now have all the options suggested by you people including cursors and joins. But which one is more optimized for this situation and how to do it?
EDIT:
select *
from A1
join A2 on [A1].[ID] = [A2].[A1ID]
join A3 on [A2].[ID] = [A3].[A2ID]
join [dbo].GetValues('232dasdg34vb3') V on A3.ID = v.Ints
You can use a cursor:
DECLARE #i INT
DECLARE cur CURSOR FAST_FORWARD READ_ONLY FOR
SELECT Ints FROM [dbo].GetValues('232dasdg34vb3')
OPEN cur
FETCH NEXT FROM cur INTO #i
WHILE ##FETCH_STATUS = 0
BEGIN
/* cursor logic -- #i will hold 232, then 34, then 3 */
FETCH NEXT FROM cur INTO #i
END
CLOSE cur
DEALLOCATE cur
If you have those IDs in another table you can just join on result of calling table valued function:
select * from SomeTable st
join [dbo].GetValues('232dasdg34vb3') ft on st.SomeID = ft.Ints
If you just need to select some records, a simple select can do the job:
DECLARE #Value VARCHAR(Max) = '232dasdg34vb3'
SELECT A1.Id, A2.Id
FROM A1
JOIN A2 ON A1.Id = A2.A1Id
JOIN A3 ON A2.Id = A3.A2Id
WHERE EXISTS (
SELECT 1
FROM [dbo].GetValues( #Value ) x
WHERE x.Ints = A3.Id
)
Don't use loops or cursors but set based approaches like:
SELECT x.Ints, ot.ID, ot.*
FROM OtherTable ot
WHERE ot.ID IN (SELECT x.Ints FROM [dbo].GetValues('232dasdg34vb3'))

Select and update in same stored procedure and using result of select to update some value

Is there anything like
select Column_n from table1;
update table2 set column_m where column_a=Column_n ;
Which can be written in "same stored" procedure
You can UPDATE with JOIN directly like this:
UPDATE t2
SET t2.column_m = ...
FROM table2 AS t2
INNER JOIN table1 AS t1 ON t1.column_n = t2.column_a;
You can put it inside a stored procedure.
SQL Server has a lot of options for updating one table using data from other tables. Bellow you could find some of these solutions:
CREATE TABLE TableX
(
ID INT NOT NULL,
Col2 VARCHAR(10)
);
INSERT TableX (ID)
SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3;
CREATE TABLE TableY
(
ID INT NOT NULL,
Col2 VARCHAR(10)
);
INSERT TableY (ID,Col2)
SELECT 1,'A' UNION ALL SELECT 2,'B' UNION ALL SELECT 4,'D';
Solutions:
-- Solution #1
UPDATE TableX SET Col2 = NULL;
UPDATE x
SET Col2 = y.Col2
FROM TableX x INNER JOIN TableY y ON x.ID = y.ID;
SELECT * FROM TableX;
/*
ID Col2
----------- ----
1 A
2 B
3 NULL
*/
-- Solution #2
UPDATE TableX SET Col2 = NULL;
UPDATE TableX
SET Col2 =
(
SELECT y.Col2
FROM TableY y
WHERE TableX.ID = y.ID
)
SELECT * FROM TableX;
/*
ID Col2
----------- ----
1 A
2 B
3 NULL
*/
-- Solution #3
UPDATE TableX SET Col2 = NULL;
WITH UpdateTableX
AS
(
SELECT x.Col2 AS TargetCol2,
y.Col2 AS SourceCol2
FROM TableX x INNER JOIN TableY y ON x.ID = y.ID
)
UPDATE UpdateTableX
SET TargetCol2 = SourceCol2
SELECT * FROM TableX;
/*
ID Col2
----------- ----
1 A
2 B
3 NULL
*/
-- Solution #4 (SQL2008+)
UPDATE TableX SET Col2 = NULL;
MERGE INTO TableX x
USING TableY y ON x.ID = y.ID
WHEN MATCHED THEN
UPDATE SET Col2 = y.Col2;
SELECT * FROM TableX;
/*
ID Col2
----------- ----
1 A
2 B
3 NULL
*/
Now, if for target table one row can match many rows from source table
then you could have problems because some solutions can become UNSAFE (solutions #1 and #3)
Example: I insert another row in source table (TableY)
INSERT TableY (ID,Col2)
SELECT 1,'AA'
Now, for one row with ID=1 from source table there are 2 rows with ID=1 ({1,'A'} and {1,'AA'}). In this case, the solutions #1 or #3 will be executed successfully even these UPDATEs are unsafe:
UPDATE TableX SET Col2 = NULL;
UPDATE x
SET Col2 = y.Col2
FROM TableX x INNER JOIN TableY y ON x.ID = y.ID;
SELECT * FROM TableX;
/*
ID Col2
----------- ----------
1 A <-- In my test, SQL Server selected "first" row with ID=1 from source table (TableY) which has 2 rows with ID=1 ({1,'A'} and {1,'AA'})
2 B
3 NULL
*/
But if I change the order of rows in the source table TableY the results of he same UPDATE statements will be different:
TRUNCATE TABLE TableY;
INSERT TableY (ID,Col2)
SELECT 1,'AA' UNION ALL SELECT 2,'B' UNION ALL SELECT 4,'D';
INSERT TableY (ID,Col2)
SELECT 1,'A';
UPDATE TableX SET Col2 = NULL;
UPDATE x
SET Col2 = y.Col2
FROM TableX x INNER JOIN TableY y ON x.ID = y.ID;
SELECT * FROM TableX;
/*
ID Col2
----------- ----------
1 AA <-- The same UPDATE gives different results: 'AA' instead of 'A'
2 B
3 NULL
*/
If I execute solutions #2 and #4 then I will receive an exception (which is good)
Solution #2:
Msg 512, Level 16, State 1, Line 17
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
The statement has been terminated.
Solution #4:
Msg 8672, Level 16, State 1, Line 55
The MERGE statement attempted to UPDATE or DELETE the same row more than once. This happens when a target row matches more than one source row. A MERGE statement cannot UPDATE/DELETE the same row of the target table multiple times. Refine the ON clause to ensure a target row matches at most one source row, or use the GROUP BY clause to group the source rows.
because I know that I have to rewrite those statements
-- Solution #2
UPDATE TableX SET Col2 = NULL;
UPDATE TableX
SET Col2 =
(
SELECT MAX(y.Col2) -- or MIN or something else
FROM TableY y
WHERE TableX.ID = y.ID
)
-- Solution #4 (SQL2008+)
UPDATE TableX SET Col2 = NULL;
MERGE INTO TableX x
USING
(
SELECT a.ID, MAX(a.Col2) AS Col2 -- or MIN or something else
FROM TableY a
GROUP BY a.ID
) y ON x.ID = y.ID
WHEN MATCHED THEN
UPDATE SET Col2 = y.Col2;
You can do this
Declare #Var = varchar(50)
select #Var = Column_n from table1;
update table2 set column_m = newValue where column_a= #Var;

Recursively update values based on rows parent id SQL Server

I have the following table structure
| id | parentID | count1 |
2 -1 1
3 2 1
4 2 0
5 3 1
6 5 0
I increase count values from my source code, but i also need the increase in value to bubble up to each parent id row until the parent id is -1.
eg. If I were to increase count1 on row ID #6 by 1, row ID #5 would increase by 1, ID #3 would increase by 1, and ID #2 would increase by 1.
Rows also get deleted, and the opposite would need to happen, basically subtracting the row to be deleted' value from each parent.
Thanks in advance for your insight.
I'm using SQL Server 2008, and C# asp.net.
If you really want to just update counts, you could want to write stored procedure to do so:
create procedure usp_temp_update
(
#id int,
#value int = 1
)
as
begin
with cte as (
-- Take record
select t.id, t.parentid from temp as t where t.id = #id
union all
-- And all parents recursively
select t.id, t.parentid
from cte as c
inner join temp as t on t.id = c.parentid
)
update temp set
cnt = cnt + #value
where id in (select id from cte)
end
SQL FIDDLE EXAMPLE
So you could call it after you insert and delete rows. But if your count field are depends just on your table, I would suggest to make a triggers which will recalculate your values
You want to use a recursive CTE for this:
with cte as (
select id, id as parentid, 1 as level
from t
union all
select cte.id, t.parentid, cte.level + 1
from t join
cte
on t.id = cte.parentid
where cte.parentid <> -1
) --select parentid from cte where id = 6
update t
set count1 = count1 + 1
where id in (select parentid from cte where id = 6);
Here is the SQL Fiddle.

compare two tables for matching and update or move and delete rows

I have two tables which I need to compare if there are values that are matching in both.
Structure of tables
Persons
ID | fname | lname | address | socialNumber | taxNumber | Match
Personals
ID | fname | lname | address | socialNumber | taxNumber
I have two parameters that are being check for matching, socialNumber AND/OR taxNumber.
Person need to be compared with Personals
If match is found update ID (in Persons) with ID of Personals (the one that is matching) and set "Match" to True (default is false)
If match is not found delete row from Persons and insert it as new row in Personals
List item
Also, If it is possible to have everything dynamic as possible if I would like to add new parameters to look for matching (example: address).
This is my SQL code that I'm using it right now but I don't know how to expand it for my needs.
UPDATE Persons SET Match = 1, ID = (SELECT MAX(ID) FROM Personals) WHERE taxNumber IN ( SELECT taxNumber FROM Personals GROUP BY taxNumber HAVING ( COUNT(taxNumber) > 1 ))
I don't think you can easily do this in a single query, not without using a cursor, but you could just break the action into two parts like these:
--update action
update a
set a.id = b.id, a.match =1
from _persons a inner join _personals b
on a.social = b.social or a.taxnumber = b.taxnumber
--delete / insert action
declare #RowCount as integer
select #RowCount = count(a.id) from _persons a
where ID not in (
select a.id
from _persons a inner join _personals b
on a.social = b.social or a.taxnumber = b.taxnumber
)
if #RowCount>0
begin
insert into _Personals (ID, fname, lname, address, social, taxnumber)
select ID, fname, lname, address, social, taxnumber from _persons a
where ID not in (
select a.id
from _persons a inner join _personals b
on a.social = b.social or a.taxnumber = b.taxnumber
)
delete _persons
where ID not in
(
select a.id
from _persons a inner join _personals b
on a.social = b.social or a.taxnumber = b.taxnumber
)
end

Categories

Resources