Updating a set of values using criteria from multiple tables - sql

Given a specific set of rows that I've queried I want to update the "ifSpeed" value on each of them to a static value of 1000000000.
I have created a query to find all the specific rows that I need to update that value in, but had to join two tables to get that information, due to the the tag I'm using being in a separate table.
I have created a query to find all the specific rows that I need to update that value in, but had to join two tables to get that information, due to the the tag I'm using being in a separate table. That query is the first block of code. The second is one of my attempts at updating the values. I've also looked into subqueries, but my SQL is very rusty as it has been about 8 years since I've worked with it.
SELECT ifSpeed
FROM master_dev.device_interfaces AS MDDI
JOIN master_dev.device_interface_tags_map AS MDDITM
ON MDDI.if_id = MDDITM.if_id
WHERE ifSpeed=10000000
AND MDDITM.tag_id=13
UPDATE master_dev.device_interfaces
SET ifSpeed = '1000000000'
FROM master_dev.device_interfaces AS MDDI
JOIN master_dev.device_interface_tags_map AS MDDITM
ON MDDI.if_id = MDDITM.if_id
WHERE ifSpeed=10000000
AND MDDITM.tag_id=13
My expectation is that all rows that exist in my SELECT query have their ifSpeed updated to 1000000000.
What actually happens is simply an error. I'm using a restricted interface to query the DB and it only provides rows affected, the data from the rows or simply "ERROR". Very helpful... I know.

in SSMS try Below
---- Updated ---
UPDATE MDDI
SET ifSpeed = '1000000000'
FROM master_dev.dbo.device_interfaces AS MDDI
JOIN master_dev.dbo.device_interface_tags_map AS MDDITM
ON MDDI.if_id = MDDITM.if_id
WHERE ifSpeed = 10000000
AND MDDITM.tag_id = 13

The syntax of UPDATE that has a FROM clause is rather cumbersome. Luckily in SQL Server there is a better way.
I usually use CTE (common-table expression) in this case, which makes the query very easy to write and read and verify that it works as intended.
So, you have your complex SELECT statement that returns all the rows that you want to update. Great.
SELECT ifSpeed
FROM
master_dev.device_interfaces AS MDDI
JOIN master_dev.device_interface_tags_map AS MDDITM
ON MDDI.if_id = MDDITM.if_id
WHERE
ifSpeed=10000000
AND MDDITM.tag_id=13
;
Now, wrap it into CTE and update the CTE:
WITH
CTE
AS
(
SELECT ifSpeed
FROM
master_dev.device_interfaces AS MDDI
JOIN master_dev.device_interface_tags_map AS MDDITM
ON MDDI.if_id = MDDITM.if_id
WHERE
ifSpeed=10000000
AND MDDITM.tag_id=13
)
UPDATE CTE
SET ifSpeed = '1000000000'
;
You can write SELECT * FROM CTE instead of the line UPDATE CTE SET ... before performing an actual update to verify that you are selecting correct rows.

Thanks for all the input on this! Unfortunately I had two different teams give me two different answers. While I supposedly was able to do this after escalating further up the chain the engineering team on the backend put up a brick wall saying don't risk the DB tables. That decision baffles me based on what's affected, but not my call to make I suppose.
That being said they did let me know that the original code was fine and likewise the variation that maSTAShuFu provided.
#Vladimir - Sorry didn't get to try your's out, but I'm definitely keeping it in my back pocket. Thank you!

Related

Request optimisation

I have two tables, on one there are all the races that the buses do
dbo.Courses_Bus
|ID|ID_Bus|ID_Line|DateHour_Start_Course|DateHour_End_Course|
On the other all payments made in these buses
dbo.Payments
|ID|ID_Bus|DateHour_Payment|
The goal is to add the notion of a Line in the payment table to get something like this
dbo.Payments
|ID|ID_Bus|DateHour_Payment|Line|
So I tried to do this :
/** I first added a Line column to the dbo.Payments table**/
UPDATE
Table_A
SET
Table_A.Line = Table_B.ID_Line
FROM
[dbo].[Payments] AS Table_A
INNER JOIN [dbo].[Courses_Bus] AS Table_B
ON Table_A.ID_Bus = Table_B.ID_Bus
AND Table_A.DateHour_Payment BETWEEN Table_B.DateHour_Start_Course AND Table_B.DateHour_End_Course
And this
UPDATE
Table_A
SET
Table_A.Line = Table_B.ID_Line
FROM
[dbo].[Payments] AS Table_A
INNER JOIN (
SELECT
P.*,
CP.ID_Line AS ID_Line
FROM
[dbo].[Payments] AS P
INNER JOIN [dbo].[Courses_Bus] CP ON CP.ID_Bus = P.ID_Bus
AND CP.DateHour_Start_Course <= P.Date
AND CP.DateHour_End_Course >= P.Date
) AS Table_B ON Table_A.ID_Bus = Table_B.ID_Bus
The main problem, apart from the fact that these requests do not seem to work properly, is that each table has several million lines that are increasing every day, and because of the datehour filter (mandatory since a single bus can be on several lines everyday) SSMS must compare each row of the second table to all rows of the other table.
So it takes an infinite amount of time, which will increase every day.
How can I make it work and optimise it ?
Assuming that this is the logic you want:
UPDATE p
SET p.Line = cb.ID_Line
FROM [dbo].[Payments] p JOIN
[dbo].[Courses_Bus] cb
ON p.ID_Bus = cb.ID_Bus AND
p.DateHour_Payment BETWEEN cb.DateHour_Start_Course AND cb.DateHour_End_Course;
To optimize this query, then you want an index on Courses_Bus(ID_Bus, DateHour_Start_Course, DateHour_End_Course).
There might be slightly more efficient ways to optimize the query, but your question doesn't have enough information -- is there always exactly one match, for instance?
Another big issue is that updating all the rows is quite expensive. You might find that it is better to do this in loops, one chunk at a time:
UPDATE TOP (10000) p
SET p.Line = cb.ID_Line
FROM [dbo].[Payments] p JOIN
[dbo].[Courses_Bus] cb
ON p.ID_Bus = cb.ID_Bus AND
p.DateHour_Payment BETWEEN cb.DateHour_Start_Course AND cb.DateHour_End_Course
WHERE p.Line IS NULL;
Once again, though, this structure depends on all the initial values being NULL and an exact match for all rows.
Thank you Gordon for your answer.
I have investigated and came with this query :
MERGE [dbo].[Payments] AS p
USING [dbo].[Courses_Bus] AS cb
ON p.ID_Bus= cb.ID_Bus AND
p.DateHour_Payment>= cb.DateHour_Start_Course AND
p.DateHour_Payment<= cb.DateHour_End_Course
WHEN MATCHED THEN
UPDATE SET p.Line = cb.ID_Ligne;
As it seems to be the most suitable in an MS-SQL environment.
It also came with the error :
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.
I understood this to mean that it finds several lines with identical
[p.ID_Bus= cb.ID_Bus AND
p.DateHour_Payment >= cb.DateHour_Start_Course AND
p.DateHour_Payment <= cb.DateHour_End_Course]
Yes, this is a possible case, however the ID is different each time.
For example, if two blue cards are beeped at the same time, or if there is a loss of network and the equipment has been updated, thus putting the beeps at the same time. These are different lines that must be treated separately, and you can obtain for example:
|ID|ID_Bus|DateHour_Payments|Line|
----------------------------------
|56|204|2021-01-01 10:00:00|15|
----------------------------------
|82|204|2021-01-01 10:00:00|15|
How can I improve this query so that it takes into account different payment IDs?
I can't figure out how to do this with the help I find online. Maybe this method is not the right one in this context.

UPDATE with EXISTS

I'm testing this UPDATE statement to update all 4%, 8%, and 9% parts in our database. I'm trying to get the QTY_MULTIPLE value to match the CASES per layer value.
So, the REGEXP_LIKE, will eventually read:
> Regexp_like ( sp.part_no, '^4|^8|^9' )
It doesn't right now because I'm testing three specific parts. I want to make sure the rest of the statement works the way that it's supposed to.
Here's what I'm testing with:
UPDATE SALES_PART_TAB sp
SET sp.qty_multiple = ( SELECT cases_per_layer
FROM HH_INV_PART_CHARS
WHERE sp.part_no = HH_INV_PART_CHARS.part_no AND
sp.contract = HH_INV_PART_CHARS.contract )
WHERE Regexp_like ( sp.part_no, '^410-0017|^816-0210|^921-0003' ) AND
EXISTS
( SELECT sp.contract,
sp.part_no,
sp.qty_multiple,
HH_INV_PART_CHARS.cases_per_layer
FROM SALES_PART sp
inner join HH_INV_PART_CHARS
ON sp.part_no = HH_INV_PART_CHARS.part_no AND
sp.contract = HH_INV_PART_CHARS.contract
WHERE sp.qty_multiple != HH_INV_PART_CHARS.cases_per_layer );
When I run this statement, it updates 16 rows.
However, I'm expecting it to update 15 rows. I reached this conclusion by running the following SELECT statement:
SELECT sp.contract,
sp.catalog_no,
sp.qty_multiple,
HH_INV_PART_CHARS.cases_per_layer
FROM SALES_PART sp
inner join HH_INV_PART_CHARS
ON sp.part_no = HH_INV_PART_CHARS.part_no AND
sp.contract = HH_INV_PART_CHARS.contract
WHERE sp.qty_multiple != HH_INV_PART_CHARS.cases_per_layer AND
Regexp_like ( sp.part_no, '^410-0017|^816-0210|^921-0003' )
I think the problem I'm having is the UPDATE statement is updating all rows where the part_no and contract from the sales_part table exist on HH_INV_PART_CHARS. It's not limiting the update to part where the qty_multiple isn't equal to the cases_per_layer (which is what I want).
I'm a little stumped right now. I've been trying to work on both the subqueries but I'm not having any luck identifying where the problem is.
The Regexp_like ( sp.part_no,...) in the update query refers to SALES_PART_TAB.spart_no, while in the second query it refers to SALES_PART.spart_no.
One of the causes of the fog is that you redefine the alias sp in the same query, and so the exists subquery does not relate in any way to the record that is being updated. This means that if you would throw away the exists condition, you would still update 16 records. It seems very unlikely that this is what you want.
Use a different alias, so you can distinguish which table you want to refer to.

Update a single column based on multiple conditions in an expression

I am trying to update all the values in a single column. The column currently contains only NULL values.
There are two columns in the table to be updated and the table the new data should be taken from that need to match for the update to take place.
After looking up the problem, it does not seem to have been mentioned before in the way it needs to be solved here.
The code I have come up with so far is (and does not work a bit):
UPDATE ANSWER_PATTERN AS outer
SET outer.ANSWER_DURATION =
(
SELECT inner.ANSWER_DURATION
FROM PREP_ANSWER_DURATION AS inner
WHERE (inner.TEST_ITEM_EXT_ID,
inner.STUDENT_EXT_ID) =
(outer.TEST_ITEM_EXT_ID,
outer.STUDENT_EXT_ID)
);
So, how can I get the values from table2 in the column ANSWER_DURATION into table1, column ANSWER_DURATION, provided the TEST_ITEM_EXT_ID and STUDENT_EXT_ID columns both match?
I would be glad for any help provided. :-)
Here is how you would do that:
UPDATE outer, inner
SET outer.ANSWER_DURATION = inner.ANSWER_DURATION
WHERE inner.TEST_ITEM_EXT_ID=outer.TEST_ITEM_EXT_ID AND
inner.STUDENT_EXT_ID=outer.STUDENT_EXT_ID;
I think this achieves what you're looking for. My experience with your database flavor is nonexistent, but this is how I would approach the problem using PostgreSQL.
UPDATE ANSWER_PATTERN t1
SET ANSWER_DURATION = t2.ANSWER_DURATION
FROM PREP_ANSWER_DURATION t2
WHERE t2.TEST_ITEM_EXT_ID = t1.TEST_ITEM_EXT_ID
AND t2.STUDENT_EXT_ID = t1.STUDENT_EXT_ID;

sql query result returns asterisk "*" as column value

I'm trying to update a temporary table with multiple values from another table without using a join.
However, the query doesn't give any error but rather returns an asterisk as the value of the column. I have googled and asked some folks around the office but no one seems to have encountered this before or can offer explanation of why this could be happening.
update ##tempCLUnique set Total =
(
select COUNT(distinct u.unique_subs)
from tbl_Cluster_Cumm_Unique_Subs u
where u.cluster = ##tempCLUnique.cluster
)
Seems simple enough
Result Screen Grabhttp://i.stack.imgur.com/qE0ER.png
Use this
update ##tempCLUnique set Total = U.unique_subs
FROM ##tempCLUnique
INNER JOIN
(
select COUNT(distinct unique_subs)unique_subs
from tbl_Cluster_Cumm_Unique_Subs
)U
ON
u.cluster = ##tempCLUnique.cluster
Change the join according to your use.
Ashutosh

MS-ACCESS: Deleting all rows except for top 1 and updating a table from a query

I'm almost done with this, just a few last hiccups. I now need to delete all records from a table except for the top 1 where readings_miu_id is the "DISTINCT" column. In other words words i need to delete all records from a table other than the first DISTINCT readings_miu_id. I am assuming all I need to do is modify the basic delete statement:
DELETE FROM analyzedCopy2
WHERE readings_miu_id = some_value
But I can't figure out how to change the some_column=some_value part to something like:
where some_column notequal to (select top 1 from analyzedCopy2 as A
where analyzedCopy2.readings_miu_id = A.readings_miu_id)
and then I need to figure out how to use an UPDATE statement to update a table (analyzedCopy2) from a query (which is where all of the values I want stored into column RSSI in table analyzedCopy2 are currently located). I've tried this:
UPDATE analyzedCopy2 from testQuery3 SET analyzedCopy2.RSSI =
(select AvgOfRSSI from testQuery3 INNER JOIN analyzedCopy2 on analyzedCopy2.readings_miu_id = testQuery3.readings_miu_id where analyzedCopy2.readings_miu_id = testQuery3.readings_miu_id)
where analyzedCopy2.readings_miu_id = testQuery3.readings_miu_id
but apparently I can't use FROM inside of an update statement. Any thoughts?
I'm sure I'm going about this a very nonstandard (and possibly if not probably the flat out wrong) way but I'm not being allowed to use vb.net2008 to pull and manipulate then store the data like I would like to so I'm stuck right now using sql statements in ms-access which is a good learning experience (Even if trying to do such odd things as I've been having to do in sql statements is making me beat my head against my deck figuratively of course)
MS Access UPDATE sql statements cannot reference queries, but they can reference tables. So the thing to do is store the query results into a table.
SELECT YourQuery.*
INTO TempTable1
FROM YourQuery
Now you can use TempTable1 in an UPDATE query:
UPDATE TargetTable
INNER JOIN TempTable1 ON TempTable1.TargetTableId = TargetTable.Id
SET TargetTable.TargetField = TempTable1.SourceField
See my answer to this question.
I don't have a copy of access on this machine, and it's been a few years since I dabbled in access, so I'm taking a wild stab here, but can you do a
delete from analyzedCopy2
where readings_miu_id not in (select top 1 readings_miu_id from analyzedCopy2 order by...)
(you'll need the order by to get the proper top 1 record, order by the id maybe?)
I've got no hope of helping you with the second one without a copy of access. I know how I'd do it in TSQL, but access is a whole different kettle of wtf's :-)
I was trying to make too complicated, since all of the records that i needed to pull had the same information in each field that i needed all i had to do was use:
SELECT DISTINCT readings_miu_id, DateRange, RSSI, ColRSSI, Firmware, CFGDate, FreqCorr, Active, OriginCol, ColID, Ownage, SiteID, PremID, prem_group1, prem_group2
FROM analyzedCopy2
ORDER BY readings_miu_id;
in order to pull the top 1 record per readings_miu_id.