UPDATE updates some, but not all rows - sql

When I run the following code with the SELECT at the bottom (Commented out in the code), I get 12 rows. Then I comment out the SELECT and uncomment the UPDATE, and run it. Then go back to SELECT, and there are four rows. 8 got updated and are no longer selected when I use SELECT, but these four persist. Switch back to UPDATE again, and there are still these four rows when I SELECT.
Any ideas?
WITH cte AS (
SELECT r.Request_ID,
rc.Route_id,
rc.Flight_Information_Region,
rc.Route AS rcRoute,
rd.FIR,
rd.Route AS rdRoute
FROM Route_Country rc
INNER JOIN Flight_Legs_Route_Header rh ON (rc.Route_ID = rh.Route_ID)
INNER JOIN Flight_Legs_Route_DETAIL rd ON (rh.Flight_Leg_Route_ID = rd.Flight_Leg_Route_ID AND rc.Flight_Information_Region = rd.FIR)
INNER JOIN Request r ON (rh.Request_ID = r.Request_ID)
WHERE rc.Route <> rd.Route
AND r.Request_Archived = 'N'
)
UPDATE cte SET rdRoute = rcRoute
--SELECT * FROM cte

If an update statement finds more than one record matching the record to be updated, it will update with the value from the first matched record. In your statement, the WHERE rc.Route <> rd.Route will allow a second set of records to be updated, if other matches exist, since the first updated set is filtered out. A third pass would again update the first set of records since the second update undid the first.
Here is a quick example of how this could happen. Execute this sql over and over again and watch the t1_value switch back and forth with the same update statement:
-- load test data
if object_id('tempdb..#test1') is null
begin
create table #test1 (id int identity,value int)
insert into #test1 (value) values(1)
end
if object_id('tempdb..#test2') is null
begin
create table #test2 (id int identity,test1_id int,value int)
insert into #test2 (test1_id,value) values(1,1),(1,2)
end
-- update with cte
;with cte as
(
select
t1.id,
t1.value as t1_value,
t2.value as t2_value
from #test1 as t1
inner join #test2 as t2
on t2.test1_id = t1.id
and t2.value <> t1.value
)
update cte set t1_value = t2_value
-- return update cte values
;with cte as
(
select
t1.id,
t1.value as t1_value,
t2.value as t2_value
from #test1 as t1
inner join #test2 as t2
on t2.test1_id = t1.id
and t2.value <> t1.value
)
select * from cte

CTEs are only good for the duration of the one query immediately following thier definition. So what you get is not related in any way to previous runs because the CTE is not persisted.
If you want to update the data, the update the table you want to change the data in not the cte. and then do the select from the CTE to see if any records are still applicable.
If you wnat to do more steps with the CTE data, then use a temp table or table variable instead.

Related

SQL Update Multiple Rows with Count from another Table

Suppose I have two tables. One table, tbl1, is "long" and non-aggregated. The structure is as follows:
Software_Name:
Word
PowerPoint
PowerPoint
Excel
Word
PowerPoint
In a second Table, tbl2, I want to summarize the data from the first table, namely the count of Software. The second Table will have a structure like:
Software_Name: Count:
Word 2
PowerPoint 3
Excel 1
I have tried:
update tbl2
set count =
(select count(software_name)
from tbl1
group by software_name
where tbl1.software_name = tbl2.software_name)
from tbl1
I get a result inserted into the proper column, but it is not the proper value. It is the sum of all values, in this case 5. I have included the where clause because in my tbl1 I have many more software_names than am interested in counting in tbl2.
UPDATE
I am using Teradata Aster for this project. I have been looking at the Aster documentation for the UPDATE command and came across this:
UPDATE [ ONLY ] table
SET column = expression [, ...]
[ FROM fromlist ]
[ WHERE condition | WHERE CURRENT OF cursor_name ];
In reading about the fromlist, I came across this bit of information:
Note that the target table must not appear in the fromlist unless you intend a
self-join (in which case it must appear with an alias in the fromlist).
You want a correlated subquery:
update tbl2
set count = (select count(*) from tbl1 where tbl1.software_name = tbl2.software_name);
Not sure what DBMS you're using but in SQL Server I would recommend a CROSS APPLY with example below...
update t2
set t2.[Count] = t1.[Count]
from t2
cross apply (
select count(*) AS [Count]
from t1
where t1.Software_name = t2.Software_name
) AS t1
You can read more about the APPLY operator here: https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/
You can try using the CTE as below:
DECLARE #TABLE1 AS TABLE (Software_Name VARCHAR(100))
INSERT INTO #TABLE1
SELECT 'Word'
UNION ALL
SELECT 'PowerPoint'
UNION ALL
SELECT 'PowerPoint'
UNION ALL
SELECT 'Excel'
UNION ALL
SELECT 'Word'
UNION ALL
SELECT 'PowerPoint'
DECLARE #TABLE2 AS TABLE (Software_Name VARCHAR(100),Cnt INT)
INSERT INTO #TABLE2 (Software_Name)
SELECT DISTINCT Software_Name FROM #TABLE1
;WITH CTE AS
(
SELECT
COUNT(T1.Software_Name) AS Cnt
,T1.Software_Name
FROM #TABLE2 T2
INNER JOIN #TABLE1 T1 ON T1.Software_Name = T2.Software_Name
GROUP BY
T1.Software_Name
)
UPDATE T2
SET
T2.Cnt = C.Cnt
FROM #TABLE2 T2
INNER JOIN CTE C ON C.Software_Name = T2.Software_Name
SELECT * FROM #TABLE2
NOTE: I am assuming you are using SQL Server. Also i am posting it as an answer as i dont have desired reputation to comment.
hope it helps!

Oracle update using subquery

I need to convert my update Ms SQL Server query for Oracle
I have two tables:
Table1(id int, SaveId int, ChangeId int)
Table2(id int, smSaved int, boldId int)
The query is:
UPDATE
Table1
Set
SaveId=tbl.smSaved
FROM
Table1 tbl1
join Table2 tbl on tbl1.ChangeId=tbl.boldId
In MsSql everything works fine, but when I'm trying to run it in Oracle thats doesn't works. I can't figure out whats wrong with it.
Can anybody explain me what I need to change?
Try this:
UPDATE table1 SET SaveId =
(SELECT tbl.saved FROM table2 tbl
WHERE tbl.boldId = table1.ChangeId)
WHERE EXISTS(
SELECT tbl.saved FROM table2 tbl
WHERE tbl.boldId = table1.ChangeId
)
The first part of query update SaveId with value of tbl.saved (I suppose you have only one row of table2 linked with table1.
The second part (exists) guarantee you have a correspond between two table (if you add exists you put NULL where not exists row in table2)
You are may to use update with subquery same MS SQL. In Oracle its look like:
UPDATE (SELECT t1.savedId t1_saved
, t2.smsaved t2_saved
FROM table1 t1
, table2 t2
WHERE t1.changeid = t2.boldid)
SET t1_saved = t2_saved

SQL Update subquery returns no results run different sub query

I am trying to do an update and i'm having problems (using microsoft sql server)
update mytable
set myvalue=
(
(select myvalue from someothertable
where someothertable.id=mytable.id)
)
from table mytable
where mytable.custname='test'
Basically the subquery could return no results if that does happen i want to call a different subquery:
(select myvalue from oldtable
where oldtable.id=mytable.id)
well, you could run the second query first, and then, the first query.
In that way, you will override the values only when the first query can bring them, and when the (original) first query won't bring any result, they will have the result of first query.
Also, I think you have a typo in your second query with the table name.
update mytable
set myvalue=
(
select myvalue from oldtable
where oldtable.id=mytable.id
)
from table mytable
where mytable.custname='test'
and exists (select 1 from oldtable
where oldtable.id=mytable.id)
update mytable
set myvalue=
(
select myvalue from someothertable
where someothertable.id=mytable.id
)
from table mytable
where mytable.custname='test'
and exists ( select 1 from someothertable
where someothertable.id=mytable.id)
edit: you will need to add the exists clause, cause if not it will update with null values, I think
You can simply join both tables,
UPDATE a
SET a.myValue = b.myValue
FROM myTable a
INNER JOIN someOtherTable b
ON a.ID = b.ID
WHERE a.CustName = 'test'

Simple update statement so that all rows are assigned a different value

I'm trying to set a column in one table to a random foreign key for testing purposes.
I attempted using the below query
update table1 set table2Id = (select top 1 table2Id from table2 order by NEWID())
This will get one table2Id at random and assign it as the foreign key in table1 for each row.
It's almost what I want, but I want each row to get a different table2Id value.
I could do this by looping through the rows in table1, but I know there's a more concise way of doing it.
On some test table my end your original plan looks as follows.
It just calculates the result once and caches it in a sppol then replays that result. You could try the following so that SQL Server sees the subquery as correlated and in need of re-evaluating for each outer row.
UPDATE table1
SET table2Id = (SELECT TOP 1 table2Id
FROM table2
ORDER BY Newid(),
table1.table1Id)
For me that gives this plan without the spool.
It is important to correlate on a unique field from table1 however so that even if a spool is added it must always be rebound rather than rewound (replaying the last result) as the correlation value will be different for each row.
If the tables are large this will be slow as work required is a product of the two table's rows (for each row in table1 it needs to do a full scan of table2)
I'm having another go at answering this, since my first answer was incomplete.
As there is no other way to join the two tables until you assign the table2_id you can use row_number to give a temporary key to both table1 and table2.
with
t1 as (
select row_number() over (order by table1_id) as row, table1_id
from table1 )
,
t2 as (
select row_number() over (order by NEWID()) as row, table2_id
from table2 )
update table1
set table2_id = t2.table2_id
from t1 inner join t2
on t1.row = t2.row
select * from table1
SQL Fiddle to test it out: http://sqlfiddle.com/#!6/bf414/12
Broke down and used a loop for it. This worked, although it was very slow.
Select *
Into #Temp
From table1
Declare #Id int
While (Select Count(*) From #Temp) > 0
Begin
Select Top 1 #Id = table1Id From #Temp
update table1 set table2Id = (select top 1 table2Id from table2 order by NEWID()) where table1Id = #Id
Delete #Temp Where table1Id = #Id
End
drop table #Temp
I'm going to assume MS SQL based on top 1:
update table1
set table2Id =
(select top 1 table2Id from table2 tablesample(1 percent))
(sorry, not tested)

How do I compare 2 rows from the same table (SQL Server)?

I need to create a background job that processes a table looking for rows matching on a particular id with different statuses. It will store the row data in a string to compare the data against a row with a matching id.
I know the syntax to get the row data, but I have never tried comparing 2 rows from the same table before. How is it done? Would I need to use variables to store the data from each? Or some other way?
(Using SQL Server 2008)
You can join a table to itself as many times as you require, it is called a self join.
An alias is assigned to each instance of the table (as in the example below) to differentiate one from another.
SELECT a.SelfJoinTableID
FROM dbo.SelfJoinTable a
INNER JOIN dbo.SelfJoinTable b
ON a.SelfJoinTableID = b.SelfJoinTableID
INNER JOIN dbo.SelfJoinTable c
ON a.SelfJoinTableID = c.SelfJoinTableID
WHERE a.Status = 'Status to filter a'
AND b.Status = 'Status to filter b'
AND c.Status = 'Status to filter c'
OK, after 2 years it's finally time to correct the syntax:
SELECT t1.value, t2.value
FROM MyTable t1
JOIN MyTable t2
ON t1.id = t2.id
WHERE t1.id = #id
AND t1.status = #status1
AND t2.status = #status2
Some people find the following alternative syntax easier to see what is going on:
select t1.value,t2.value
from MyTable t1
inner join MyTable t2 on
t1.id = t2.id
where t1.id = #id
SELECT COUNT(*) FROM (SELECT * FROM tbl WHERE id=1 UNION SELECT * FROM tbl WHERE id=2) a
If you got two rows, they different, if one - the same.
SELECT * FROM A AS b INNER JOIN A AS c ON b.a = c.a
WHERE b.a = 'some column value'
I had a situation where I needed to compare each row of a table with the next row to it, (next here is relative to my problem specification) in the example next row is specified using the order by clause inside the row_number() function.
so I wrote this:
DECLARE #T TABLE (col1 nvarchar(50));
insert into #T VALUES ('A'),('B'),('C'),('D'),('E')
select I1.col1 Instance_One_Col, I2.col1 Instance_Two_Col from (
select col1,row_number() over (order by col1) as row_num
FROM #T
) AS I1
left join (
select col1,row_number() over (order by col1) as row_num
FROM #T
) AS I2 on I1.row_num = I2.row_num - 1
after that I can compare each row to the next one as I need