Update rows content adding values from rows in another table - database

I've already tried all things from here: Update rows from another table
with no success.
I'm using SQLite, and I'm executing the query with SQLite Database Browser.
I have 3 tables Off, Vio and Loc with data like this
Off(Aid,ox,oy):
0 0 0
1 100 100
2 200 200
Vio(Vid,Aid):
0 0
1 1
2 2
3 1
Loc(vid,x,y):
0 1 2
1 5 6
2 9 1
2 2 3
3 4 4
Aid is primary key in Off and foreign key in Vio.
Vid is primary key in Vio and foreign key in Loc.
I want to update the Loc rows adding the correct ox and oy to the x and y value to get something like this as a result:
"updated" Loc(vid,x,y)
0 1 2
1 105 106
2 209 201
2 202 203
3 104 104
I've tried this:
WITH CTE AS (
SELECT Loc.x, Loc.y, Off.ox AS offx, Off.oy AS offy
FROM Loc, Vio, Off
WHERE Vio.Aid=Off.Aid AND Vio.vid=Loc.vid
)
UPDATE CTE
SET x= x + offx, y= y + offy
And this:
UPDATE Loc, Off
SET x=x+Off.ox, y=y+Off.oy
FROM (
SELECT Off.ox, Off.oy
FROM Loc, Vio, Off
WHERE Vio.Aid = Off.Aid AND Vio.vid=Loc.vid
)
Within SQLite Database Browser with no success even when the SELECT query gives me all the data I need for the update.

As mentioned in the duplicate question, you have to use a correlated subquery. Since you're updating two columns, you need to use a subquery for each of them:
UPDATE Loc
SET x = (
SELECT L1.x + Off.ox
FROM Loc l1
JOIN Vio ON L1.vid = Vio.vid
JOIN Off ON Vio.Aid = Off.Aid
WHERE Loc.vid = l1.vid),
y = (
SELECT L1.y + Off.oy
FROM Loc l1
JOIN Vio ON L1.vid = Vio.vid
JOIN Off ON Vio.Aid = Off.Aid
WHERE Loc.vid = l1.vid)
DEMO

Related

How do I get "no" calculated with complex calculations and conditions in SQLite3

If I have a table like
no
size
name
1
30
toys
2
23
shelf
3
50
monitor
4
62
carrier
5
51
books
6
45
electrics
If there is a size limit and you have to delete it from the beginning of a certain "no"
Is there any sqlite3 query to get a certain "no"?
example,
limited size : 210
I should get "no" : 2 ( id 3 + id 4 + id 5 + id 6 = 208 )
Use a correlated subquery in the WHERE clause of the DELETE statement like this:
DELETE FROM tablename AS t1
WHERE (SELECT SUM(CASE WHEN t2.no >= t1.no THEN t2.size END) FROM tablename t2) > ?;
Change ? to 210 or any other limit that you want.
See the demo.

how to update large number of records as a batch of n number of records

suppose I have 100000 records in A table and 1000 records in B table. both have primary/foreign key relationship. now i want to update a column value for first 100 records in table A with column value from table B first record. similary i want to update all the 100000 records in table A as a batch 100 records for 1000 times with values from table B.
no. of records updated per batch is 100 i.e. 100000/1000=100
Lets assume you have table_a with 20 rows with a unique id column and you want to update the value column:
CREATE TABLE table_a (id, value) AS
SELECT LEVEL, CAST(NULL AS NUMBER(8,0)) FROM DUAL CONNECT BY LEVEL <= 20;
And table_b with 5 rows containing the values you want to update from:
CREATE TABLE table_b (id, value) AS
SELECT LEVEL, LEVEL FROM DUAL CONNECT BY LEVEL <= 5;
Then, you can use a correlated UPDATE statement:
UPDATE table_a a
SET value = (SELECT value
FROM table_b b
WHERE CEIL(a.id*5/20) = b.id);
or a MERGE statement:
MERGE INTO table_a a
USING table_b b
ON (CEIL(a.id*5/20) = b.id)
WHEN MATCHED THEN
UPDATE
SET value = b.value;
Both statements result in:
ID
VALUE
1
1
2
1
3
1
4
1
5
2
6
2
7
2
8
2
9
3
10
3
11
3
12
3
13
4
14
4
15
4
16
4
17
5
18
5
19
5
20
5
db<>fiddle here

SQL Server: Recursive table update

I have a table, let's say TestTable. This table has below columns:
ID1 | ID2 | ID3 | LEVEL | PARENT_LEVEL | ENABLED | OBSOLET
All columns are integers, ENABLED and OBSOLET only two possible values (0 or 1)
LEVEL Column can have a parent level, thi parent level another parent level and so on, for example, imagine following table content:
ID1 | ID2 | ID3 | LEVEL | PARENT_LEVEL | ENABLED | OBSOLET
1 6 7 98 NULL 1 0
1 6 6 99 98 1 0
1 4 6 100 99 1 0
1 2 3 200 100 1 0
2 4 1 300 NULL 0 0
3 3 4 400 NULL 0 1
3 4 5 500 400 0 0
ID1, ID2 and ID3 is the primary key.
So representing this in a tree:
+ 98
|__ 99
|__ 100
|___ 200
+ 300
+ 400
|__ 500
200 has 100 as parent, 100 has 99 as parent and 99 has 98 as parent.
300 has no parent.
500 has 400 as parent and 400 has no parent.
So what I need is an update query to update recursively the field 'ENABLED', for example:
If I update LEVEL 99 with ENABLED=1, also his parent, 98 must be updated to ENABLED=1 but not LEVELs 100 and 200.
If I update LEVEL 200 with ENABLED=1, also his parent, 100 must be updated with ENABLED=1, and also LEVELs 99 and 98, because they have parents as well.
If I update LEVEL 300 with ENABLED=1, only LEVEL 300 is updated because it has no parent.
So I need a recursive update query to update field ENABLED until LEVEL has no parent (PARENT_LEVEL). Also I need to update all the levels at once with one update query, not only execute update for a concret level.
Furthermore, on each update I need to check field 'OBSOLET', and if a LEVEL has field OBSOLET set to 1 it means that rollback has to be made, for example, taken into account above table content, if I update LEVEL 500 to ENABLED=1, no problem because its OBSOLET field is 0, so its field ENABLED is set to 1, then by recursive, we try to update its parent, LEVEL 400, to ENABLED=1, but as its OBSOLET field is set to 1 it means rollback needs to be made, that is, ENABLED field for LEVEL 400 is kept to 0 (not updated) and field ENABLED for level 500 that was set to 1 should be reverted to 0 as well.
The final problem is that this update query should be within a trigger on this table TestTable:
CREATE TRIGGER [dbo].[TG_TestTable]
ON [dbo].[TestTable]
FOR UPDATE
AS
IF UPDATE ([ENABLED])
BEGIN
// Update query must be here, so if field ENABLED is updated, trigger is fired again...so I don't know if disable trigger statement is necessary to be done before this update query and enable trigger after it.
END
This is because to activate the trigger, an update is performed on some rows of the table TestTable, for example:
UPDATE [dbo].[TestTable]
SET ENABLED = 1
WHERE
LEVEL IN (100,300,500);
so I have tried to make the update query within the trigger but I do not know how to finish it:
UPDATE [dbo].[TestTable]
SET ENABLED= inserted.ENABLED
..... // SOMETHING ELSE
FROM inserted
WHERE
[dbo].[TestTable].ID1 = inserted.ID1
AND
[dbo].[TestTable].ID2 = inserted.ID2
AND
[dbo].[TestTable].ID3 = inserted.ID3
AND
[dbo].[TestTable].PARENT_LEVEL = inserted.LEVEL;
So how can I achieve this? maybe using a recursive function or recursive CTE? or is better a recursive trigger on same table in terms of time execution and performance? All ideas will be welcome.
I've found a solution for this. It's quite a long one, but it's fairly easy to understand.
First, Create and populate sample table (Please save us this step in your future questions)
CREATE TABLE TestTable
(
ID1 int NOT NULL,
ID2 int NOT NULL,
ID3 int NOT NULL,
LEVEL int NOT NULL,
PARENT_LEVEL int,
ENABLED int,
OBSOLET int,
PRIMARY KEY (ID1, ID2, ID3)
)
INSERT INTO TestTable VALUES
(1, 6, 7, 98, NULL, 1, 0),
(1, 6, 6, 99, 98 , 0, 0),
(1, 4, 6, 100, 99 , 0, 0),
(1, 2, 3, 200, 100 , 0, 0),
(2, 4, 1, 300, NULL, 0, 0),
(3, 3, 4, 400, NULL, 0, 1),
(3, 4, 5, 500, 400 , 0, 0)
Then, Create an INSTEAD OF UPDATE trigger, that will only update the records matching your criteria.
Note: This will also update records where the enabled value was not changed, You'll see it in the code soon.
Answer code
CREATE TRIGGER tr_TestTable_IOU ON TestTable
INSTEAD OF UPDATE
AS
;WITH CTE AS
( -- A recursive cte to get all the parents of the updated records
SELECT i.ID1,
i.ID2,
i.ID3,
i.LEVEL,
i.PARENT_LEVEL,
i.ENABLED,
i.OBSOLET
FROM inserted i
INNER JOIN deleted d ON i.ID1 = d.ID1
AND i.ID2 = d.ID2
AND i.ID3 = d.ID3
WHERE i.ENABLED = 1
AND d.ENABLED = 0
-- The where clause will allow only records where enabled was changed from 0 to 1
UNION ALL
SELECT t.ID1,
t.ID2,
t.ID3,
t.LEVEL,
t.PARENT_LEVEL,
t.ENABLED,
t.OBSOLET
FROM TestTable t
INNER JOIN CTE ON t.LEVEL = CTE.PARENT_LEVEL
), CTE_OBSOLET AS
( -- A second recursive cte to get all the records where at least in one parent the value of OBSOLET = 1
SELECT i.ID1,
i.ID2,
i.ID3,
i.LEVEL,
i.PARENT_LEVEL,
i.ENABLED,
i.OBSOLET
FROM TestTable i
WHERE OBSOLET = 1
UNION ALL
SELECT t.ID1,
t.ID2,
t.ID3,
t.LEVEL,
t.PARENT_LEVEL,
t.ENABLED,
1
FROM TestTable t
INNER JOIN CTE_OBSOLET ON t.PARENT_LEVEL = CTE_OBSOLET.LEVEL
)
-- Update the enabled column to all relevant records (including parents)
UPDATE t
SET ENABLED = 1
FROM TestTable t
INNER JOIN CTE ON t.ID1 = CTE.ID1
AND t.ID2 = CTE.ID2
AND t.ID3 = CTE.ID3
LEFT JOIN CTE_OBSOLET ON t.ID1 = CTE_OBSOLET.ID1
AND t.ID2 = CTE_OBSOLET.ID2
AND t.ID3 = CTE_OBSOLET.ID3
WHERE CTE_OBSOLET.LEVEL IS NULL -- Assuming the LEVEL is not nullable. Any other not nullable column can be used here
-- Update records where columns other then ENABLED was changed.
-- Since this is an instead of update trigger, you have to include this to enable updates on other columns.
-- This assumes that you can't update the columns of the primary key (ID1, ID2 and ID3).
UPDATE t
SET LEVEL = i.LEVEL,
PARENT_LEVEL = i.PARENT_LEVEL,
OBSOLET = i.OBSOLET
FROM TestTable t
INNER JOIN inserted i ON t.ID1 = i.ID1
AND t.ID2 = i.ID2
AND t.ID3 = i.ID3
INNER JOIN deleted d ON i.ID1 = d.ID1
AND i.ID2 = d.ID2
AND i.ID3 = d.ID3
WHERE i.LEVEL <> d.LEVEL
OR d.PARENT_LEVEL <> i.PARENT_LEVEL
OR d.OBSOLET <> i.OBSOLET
GO
Testing:
SELECT *
FROM TestTable
Results:
ID1 ID2 ID3 LEVEL PARENT_LEVEL ENABLED OBSOLET
1 6 7 98 NULL 1 0
1 6 6 99 98 0 0
1 4 6 100 99 0 0
1 2 3 200 100 0 0
2 4 1 300 NULL 0 0
3 3 4 400 NULL 0 1
3 4 5 500 400 0 0
Do a couple of updates:
UPDATE TestTable
SET ENABLED = 1
WHERE LEVEL IN(200, 500)
UPDATE TestTable
SET ENABLED = 1,
OBSOLET = 1
WHERE LEVEL = 500
Test results:
SELECT *
FROM TestTable
Results:
ID1 ID2 ID3 LEVEL PARENT_LEVEL ENABLED OBSOLET
1 6 7 98 NULL 1 0
1 6 6 99 98 1 0
1 4 6 100 99 1 0
1 2 3 200 100 1 0
2 4 1 300 NULL 0 0
3 3 4 400 NULL 0 1
3 4 5 500 400 0 1

Transitive Group Query on 2 Columns in SQL Server

I need help with a transitive query in SQL Server.
I have a table with [ID] and [GRPID].
I would like to update a third column [NEWGRPID] based on the following logic:
For each [ID], get its GRPID;
Get all of the IDs associated with the GRPID from (1);
Set [NEWGRPID] equal to an integer (variable that is incremented by 1), for all of the rows from step (2)
The idea is several of these IDs are "transitively" linked across different [GRPID]s, and should all be having the same [GRPID].
The below table is the expected result, with [NEWGRPID] populated.
ID GRPID NEWGRPID
----- ----- ------
1 345 1
1 777 1
2 777 1
3 345 1
3 777 1
4 345 1
4 999 1
5 345 1
5 877 1
6 999 1
7 877 1
8 555 2
9 555 2
Try this code:
IF OBJECT_ID('tempdb..#tmp') IS NOT NULL
BEGIN
DROP TABLE #tmp;
END;
SELECT GRPID, count (*) AS GRPCNT
INTO #tmp
FROM yourtable
GROUP BY GRPID
UPDATE TGT
SET TGT.NEWGRPID = SRC.GRPCNT
FROM yourtable TGT
JOIN #tmp ON #tmp.GRPID = TGT.GRPID
If the values are likely to change over time you should think about a computed column or a trigger.

TSQL Selection Criteria For Join - Left Join

Select *
From cacheAttendanceMeasures cam
Left Join dmcUserSelectedAttendanceMeasures usam on usam.attMeasureID = cam.attMeasureID
And usam.personID = #personID
And usam.pageID = #pageID
I understand that the above query will return all rows from cacheAttendanceMeasures as well as any rows from dmcUserSelectedAttendanceMeasures and dmcUserSelectedStudentMonths where the conditions match. What I would really like it to do is this:
If the left join(s) don't match, then return all rows from cacheAttendanceMeasures -- so what it is currently doing
However, if the left join(s) DO match, then return ONLY the rows that match from cacheAttendanceMeasures
Is this possible?
EDIT:
I simplified the query above to only include one Left Join table, I don't want to over-complicate the issue.
Below are dataset examples I'd like to see returned based on if there are no matches between the tables versus there is a match:
cam Table
cID attMeasureID Value
1 1 530
2 2 95.7
3 3 380
4 4 742.57
5 5 200
usam Table
uID personID pageID attMeasureID
1 877450 31 1
2 923450 28 2
3 877450 31 3
4 369842 28 4
5 212193 25 1
Dataset to Return if #personID = 577597 & #pageID = 20:
CID attMeasureID Value uID pageID attMeasureID
1 1 530 null nul null
2 2 95.7 null null null
3 3 380 null null null
4 4 742.57 null null null
5 5 200 null null null
Dataset to Return if #personID = 877450 & #pageID = 31:
CID attMeasureID Value uID personID pageID attMeasureID
1 1 530 1 877450 31 1
3 3 380 3 877450 31 3
I'm not really sure what results you want...
At first I thought maybe something like this..
SELECT *
FROM cacheAttendanceMeasures cam
LEFT JOIN dmcUserSelectedAttendanceMeasures usam
on usam.attMeasureID = cam.attMeasureID
And usam.personID = #personID
And usam.pageID = #pageID
LEFT JOIN dmcUserSelectedStudentMonths ussm
on ussm.monthSeq = cam.pupilMonth
And ussm.personID = USAM.PersonID
And ussm.pageID = USAM.PageId
--This where clause would seem silly as it negates the left joins; making them inner joins and violates #1 as to what you're after. so I don't think that's what you're after...
WHERE ussm.personID is not null
and usam.personID is not null
So with rule 2 are you saying... if a single record isn't null, then only return records which exist in both Left joined tables and the 1st table?
So given:
T1 T2 T3
X X X
Y NULL Y
Z Y NULL
T NULL NULL
I think you would want just record X
But Given
So given:
T1 T2 T3
X X NULL
Y NULL Y
Z Y NULL
T NULL NULL
I think you would want X, Y Z, and T... need better clairification.

Resources