Microsoft SQL Trigger: Will multiple join records pointing to the same record fail to update? - sql

Background:
I have a set of products for each numbered week starting at 1.
I have a product "library" which is week zero holding the last saved product from any time period.
I have a trigger that fires upon update or insert which keeps the "library" item up to date from the inserted items.
Since there can be duplicate products in the same event using a "sequence" field, my join will create multiple records to update from, which target the same library record.
Some questions:
Will these multiples fail the update command?
Is there a better way to update the single library product?
Code:
-- PK is ID, Week #, and Sequence #
update p set p.name = i.name
from product p join inserted i on
p.id = i.id and p.week = 0 and p.sequence = 1
Note: "inserted" can have multiple events. ID is like a UPC, Week is an identity, and Sequence is like an identity, but starts at 1 for each week. You can have a sequence of 2 while not having a sequence of 1 because they can delete products.
Sample Data:
ID WeekSequence Name
12345 1 3 Lego inserted first
12345 2 2 Lego Toy inserted second
12345 2 3 Lego Toy inserted second
Result data:
ID WeekSequence Name
12345 0 1 Lego Toy Was "Lego" now is "Lego Toy"

According to this BOL entry:
Use caution when specifying the FROM
clause to provide the criteria for the
update operation. The results of an
UPDATE statement are undefined if the
statement includes a FROM clause that
is not specified in such a way that
only one value is available for each
column occurrence that is updated,
that is, if the UPDATE statement is
not deterministic.
In other words, if an UPDATE statement uses a FROM clause that has multiple rows meeting the criteria to update a single row, it is unknown which row of new data will be used. In your sample data, it is unknown whether the result was updated from the name in the row with Sequence=2 or Sequence=3.
So, if it doesn't matter which row is used for the update, what you're currently doing will work just fine. If this is a problem however, you need to write your update's FROM and WHERE clauses so that only one row is returned for each item, possibly something like the following:
;with insert2 as (
select id, week, sequence, name,
row_number() over(partition by id order by week desc, sequence desc) as [descOrd]
from inserted
)
update p
set p.name = i.name
from product p
join insert2 i on p.id = i.id and p.week = 0 and p.sequence = 1
where i.descOrd=1

Related

Find a single row and update it with nested queries

Good evening everyone, I'm trying to do an update on a Table but I can't really make it work
The feature needed is:
-Watch a field on a form, it contains the number of people that need to sit at the restaurant table.
-Find the first free table that has enough seats, set it as busy and assign a random waiter
Any idea?
more db infos:
Table "Waiters" is composed by ID(Autonumber),Name(Short Text). Has 2 names atm
Table "Tables" is composed by ID(Autonumber),Seats(Number),Busy(y/n),Waiter(short text). All tables have a fixed number of seats and have no Waiter + not busy
SOLUTION:
In the end i used "First" for the assignment and it works perfectly as it follows:
UPDATE Tables SET Tables.Waiter = DLookUp("FirstName","TopWtr")
WHERE ID IN (SELECT FIRST (ID)
FROM Tables
WHERE Seats >= Val(Forms!Room!Text12) AND Waiter Is Null);
Top wasn't working because it was returning multiple records - every table with same number of seats - and couldn't make it work with DISTINCT. This works probably because the table is already ordered by seats
Thanks to June7 for the input
Cannot SET a field value to result of a SELECT subquery - SELECT returns a dataset not a single value. Can return a single value with domain aggregate function.
Build a query object named TopWtr:
SELECT Top 1 ID FROM Waiters ORDER BY Rnd(ID);
Then use DLookup to pull that value. The Busy field seems redundant because if table has a waiter assigned that would indicate busy.
UPDATE Tables SET Tables.Waiter = DLookUp("ID","TopWtr"), Tables.Busy = True
WHERE ID IN (SELECT TOP 1 ID FROM Tables
WHERE Seats >= Val(Forms!Room!Testo17) AND Waiter Is Null
ORDER BY Seats)
An INNER JOIN may be preferable to WHERE clause:
UPDATE Tables INNER JOIN (SELECT TOP 1 ID FROM Tables
WHERE Seats >= Val(Forms!Room!Testo17) AND Waiter Is Null
ORDER BY Seats) AS T1
ON Tables.ID = T1.ID
SET Tables.Waiter = DLookUp("ID","TopWtr"), Tables.Busy = True

Best practice to find emails and phones related in a customers table

Let say I have the following customers table:
row
email
Phone
group
1
Dan#gmail.com
+135698887445
1
2
Danny#gmail.com
+145698834549
2
I would like to group customers that have the same email or same phone (or both).
The tricky part is that with incremental loading once a new customer row is been added, earlier identities that grouped to different groupIds could now be joined to the same group like so:
row
email
Phone
group
1
Dan#gmail.com
+135698887445
1
2
Danny#gmail.com
+145698834549
2--changed to group 1
3
Dan#gmail.com
+145698834549
1
In the last example you can notice that the third row has email from the first row and cellphone from the second row so it links the above rows to the same group.
How should this be done in TSQL? And is it possible to keep the groupids numbers while changing early group number only when needed?
First, define that if multiple rows are actually of the same group, the smallest group will prevail.
We then just need to loop updates until no group decrease is found:
declare #rc bigint=1 -- to go through first iteration check
declare #counter_safety_max=100
declare #counter=1
while(#rc>0 and #counter<#counter_safety_max)
begin
set #counter+=1
update current
set group=lowest.group
from
customer current
cross apply
(
select top 1 group
from customer cl
where (cl.phone=current.phone or cl.email=current.email) and cl.group<current.group
order by group asc
) as lowest
set #rc=##rowcount
end

How do I update every row of a particular column with values from another column?

I am trying to update a column called software_id with a possible three values from a lookup table. So update user softwares software id column with the values from the softwares table of the id column. But there are only three rows in the softwares table. In the user softwares table there are over 1000. But when I run the query below only three rows get updated and the rest are left as null.
So the softwares id column is number and goes 1,2,3 and the user_sofwares software_id column is all null.
When I run the following query.
UPDATE user_software c
SET sotware_id = (
SELECT DISTINCT id
FROM softwares
WHERE id = c.id
);
Only the first three rows of the destination table get updated. I want every row on the user_softwares column to be updated with either 1,2 or 3. So it should be 1,2,3,1,2,3 for example.
I keep getting this error whenever i tried to update all rows
Error report - ORA-01427: single-row subquery returns more than one row
You can do this with a MERGE statement:
MERGE INTO (SELECT software_id, 1, ora_hash(ROWID, 2) + 1 AS fake_id
FROM user_software) u_soft
USING (SELECT DISTINCT id
FROM softwares) sftw
ON (sftw.id = u_soft.fake_id)
WHEN MATCHED THEN UPDATE SET u_soft.software_id = sftw.id;
(considering your matches are unique)

SQL-Oracle: Updating table multiple row based on values contained in the same table

I have one table named: ORDERS
this table contains OrderNumber's which belong to the same person and same address lines for that person.
However sometimes the data is inconsistent;
as example looking at the table screenshot: Orders table with bad data to fix -
you all can noticed that orderNumber 1 has a name associated to and addresses line1-2-3-4. sometimes those are all different by some character or even null.
my goal is to update all those 3 lines with one set of data that is already there and set equally all the 3 rows.
to make more clear the result expected should be like this:
enter image description here
i am currently using a MERGE statement to avoid a CURSOR (for loop )
but i am having problems to make it work
here the SQL
MERGE INTO ORDERS O USING
(SELECT
INNER.ORDERNUMBER,
INNER.NAME,
INNER.LINE1,
INNER.LINE2,
INNER.LINE3,
INNER.LINE4
FROM ORDERS INNER
) TEMP
ON( O.ORDERNUMBER = TEMP.ORDERNUMBER )
WHEN MATCHED THEN
UPDATE
SET
O.NAME = TEMP.NAME,
O.LINE1 = TEMP.LINE1,
O.LINE2 = TEMP.LINE2,
O.LINE3 = TEMP.LINE3,
O.LINE4 = TEMP.LINE4;
the biggest issues i am facing is to pick a single row out of the 3 randomly - it does not matter whihc of the data - row i pick to update the line/s
as long i make the records exaclty the same for an order number.
i also used ROWNUM =1 but it in multip[le updates will only output one row and update maybe thousand of lines with the same address and name whihch belong to an order number.
order number is the join column to use ...
kind regards
A simple correlated subquery in an update statement should work:
update orders t1
set (t1.name, t1.line1, t1.line2, t1.line3, t1.line4) =
(select t2.name, t2.line1, t2.line2, t2.line3, t2.line4
from orders t2
where t2.OrderNumber = t1.OrderNumber
and rownum < 2)

SQL Select WHERE duplicate in one column x AND column y != z

So we had a duplicate SQL scripts running on our server and didn't realize it till just recently. Essentially I have many rows where there are 2 entries with the same column x (crn).
The initially got entered with the same column y (status) as well. Our application has users update the column y (status). However now we have 2 rows one with a status of 'S' and one with a status of something other than 'S'. My goal:
DELETE everything from the table WHERE there is a duplicate CRN and the STATUS is S. I don't want to delete rows unless there is a duplicate, but if there is, I only want to delete the row with a status of 'S'. Also, I'd rather not delete both records if both have a status of S, but if I do, that isn't such a big deal because I will get the courses again in the next download.
I have started making a select statement to query the rows I want, but don't know how to do the ONLY SELECT IF DUPLICATE EXISTS part. I feel like I need to UNION or LEFT JOIN or something to only get records if a duplicate exists.
SELECT * FROM
cas_usuECourses
WHERE
crn IN (SELECT crn FROM cas_usuECourses GROUP BY crn having count(1) > 1)
AND status = 'S'
AND termCode = 201320
EDIT: Is there a way to say... the above, but if both dups have 'S' only delete one of them?
EDIT: I "think" this looks good to me. Any thoughts?
SELECT id FROM (
SELECT id, Row_Number() Over (Partition By crn ORDER BY id DESC) as ranking
FROM cas_usuECourses
WHERE status = 'S'
AND termCode = 201320
) as ranking
WHERE ranking = 1
I think this will give me all the ids where the status is 'S', and if there are two with 'S' this will give me the one that was created second. I found out that every entry in our termCode has duplicates, so... don't need to worry about checking for the duplicates.
If you could add one column to your table and fill it with distinct values, it would be trivial - you could target each row.
Otherwise, after your initial step, I would generally open a cursor on your subselect with status S to select only crn's where both statuses are 'S', and in each loop iteration delete top 1 record with appropriate crn. That way you can get rid of duplicate crn/status pairs.