How to update based on original values in SQL - sql

I'm trying to do a very simple update of table where I want to substitute one string for another in a large table. I'm trying to do it in batches because the table has hundreds of millions of rows.
But I need to replace the string differently depending on what was there, but still meet the requirement of the batch number being in the most recent timestamp.
I don't want to run 5 different scripts separately.
I've gotten this far, but I don't know how to apply the update correctly depending on what was there originally.
Any ideas on how to do it efficiently?
DECLARE #batchsize bigint = 1000;
WHILE 1 = 1
BEGIN
UPDATE TOP (#batchsize) Table1
SET Row1 = 'To1' -- want to set To2, To3, To4, To5, etc. depending on what was in there already
FROM (SELECT TOP (#batchsize) Id
FROM Table1
ORDER BY TimeStamp DESC) tto
WHERE Table1.Row1 in ('From1', 'From2', 'From3', 'From4', 'From5') AND Table1.Id = tto.Id;
if ##ROWCOUNT < #batchsize
BEGIN
PRINT('All Done');
BREAK;
END;
END;

Seems like this should do it:
SET Row1 = CASE
WHEN Row1 = 'From1' THEN 'To1'
WHEN Row1 = 'From2' THEN 'To2'
etc
END

Are you simply looking for a case expression?
UPDATE TOP (#batchsize) Table1
SET Row1 = (CASE table1.Row1
WHEN 'From1' THEN 'To1'
WHEN 'From2' THEN 'To2'
WHEN 'From3' THEN 'To3'
WHEN 'From4' THEN 'To4'
WHEN 'From5' THEN 'To5'
END)
FROM (SELECT TOP (#batchsize) Id
FROM Table1
ORDER BY TimeStamp DESC
) tto
WHERE Table1.Row1 in ('From1', 'From2', 'From3', 'From4', 'From5') AND
Table1.Id = tto.Id;

Related

Multiple If Exists with AND and OR

I want to run a set of queries only if a few conditions are true. Please see one example below, I want to combine first 2 conditions and if they are true then enter begin block or else if only 3rd condition is true then enter begin block.
(If Exists(select top 1 * from table1 where [dateInTable]=#date )
and exists (select top 1 * from table2 where [dateInTable]=#date ))
-- Either above 2 are true collectively
OR
-- Or this should be true Individually
(IF exists(select top 1 * from table3 where [dateInTable]=#date))
Begin
-- Logic here
END
if i wrap these in parentheses it won't work and if i remove parentheses it might not consider OR condition as an individual condition and will discard if first 2 conditions are not true. that means 3rd condition will only be evaluated if first 2 are true.
Is this the logic you want?
if ( (exists (select 1 from table1 where [dateInTable] = #date) and
exists (select 1 from table2 where [dateInTable] = #date)
) or
exists(select 1 from table3 where [dateInTable] = #date)
)
begin
-- Logic here
end;
Note that select top 1 * in an exists is just wasted typing. The top does nothing. EXISTS just checks if a row is returned; the contents of the row are irrelevant. So, I just use 1 because it is easy to type.

SQL Assign a variable in a SELECT and do a CASE with the variable

Is there any way in doing something like this
declare a variable, assign a value to it in a select statement, then use it in a case ?
Something like this
DECLARE #result BIGINT;
SELECT #result = (SELECT count(_t.Id) FROM T _t WHERE _t.T2Id = _t2.Id GROUP BY _t.T2Id)
CASE WHEN #result IS NULL THEN 0 ELSE #result END AS ColNAme
FROM T2 _t2
The idea is that I would like to avoid doing the select count query twice.
CASE
WHEN (SELECT COUNT(_t.Id) FROM T _t WHERE _t.T2Id = _t2.Id GROUP BY _t.T2Id) IS NULL THEN 0
ELSE (SELECT COUNT(_t.Id) FROM T _t WHERE _t.T2Id = _t2.Id GROUP BY _t.T2Id) END AS ColNAme
I don't want to use functions.
The result set should contain the count of all T items that have a T2 reference by T2 Id
ID | Count
2 | 0
4 | 12
Yes, you can use a variable defined in one select later in the script. But you still use two select queries (note the following doesn't work so read the whole answer):
DECLARE #result BIGINT;
SELECT #result = (SELECT count(_t.Id) FROM T _t WHERE _t.T2Id = _t2.Id GROUP BY _t.T2Id);
SELECT (CASE WHEN #result IS NULL THEN 0 ELSE #result END) AS ColNAme
FROM T2 _t2;
Notes:
The first query will return an error because _t2 is not defined.
The second query should just use COALESCE(#result, 0).
#result will never be NULL, because COUNT() never returns a NULL value. So the second query could really be just select #result.
Returning the same value for every row in T2 is strange. I imagine your actually query is more interesting.
There is no reason to use a variable, unless you need the value later in the script.
So, based on your desired results, I think you simply want a left join and group by:
select t2.id, count(t.id)
from t2 left join
t
on t2.id = t.t2id
group by t2.id;

How to update the Top (20) records with using Select Query

How to update the Top (20) records with using Select query?
Table1 (table records are in for loop) has 60 records. At a time, I want to get first 20 records based on table column="TEXT", then update those 20 records with column="TEXT1".
After that, I will pick the next 20 records (21-40) and again update as above.
I'm using below query, but it will update the first 20 records after next 20 (21-40) records it will not work.
Update tableName set Column = 'TEXT1' where column = 'TEXT' ;
or if need be conditionally update...
UPDATE tablename set column = case when column = 'TEXT' then 'Text1'
else column = 'OtherText' then 'Text1Other'
else column = 'StillOtherText' then 'Text1Other2' end
WHERE column in ('TEXT','OtherText','StillOtherText');
One way is to use a while-loop that checks for the existence of 'TEXT'. If the check returns true, then the top 20 primary keys are selected as part of an update statement.
WHILE EXISTS (SELECT *
FROM Table1
WHERE yourcolumn = 'TEXT')
BEGIN
UPDATE Table1
SET yourcolumn = 'TEXT1'
WHERE primarykey IN (
SELECT TOP 20 primarykey
FROM Table1
WHERE yourcolumn = 'TEXT'
)
END

Update multiple columns in MERGE statement ORACLE

I want to update multiple columns in MERGE statement,but for each column the conditions are different.How can I achieve it.
I have more than 1 million rows in both the tables.All columns are number.Except Id all 3 columns have number with precision around 18 digits
eg: 1.34255353433230675
Is there a better way to Update.Around 50,000 rows might update daily So I have to merge the updates values to target table.
I tried UPDATE and FORALL but its slow.
I basically want to merge the difference based on common ID column.Any other approach is better?
DECLARE
TYPE test1_t IS TABLE OF test.score%TYPE INDEX BY PLS_INTEGER;
TYPE test2_t IS TABLE OF test.id%TYPE INDEX BY PLS_INTEGER;
TYPE test3_t IS TABLE OF test.Crank%TYPE INDEX BY PLS_INTEGER;
TYPE test4_t IS TABLE OF test.urank%TYPE INDEX BY PLS_INTEGER;
vscore test1_t;
vid test2_t;
vcrank test3_t;
vurank test4_t;
BEGIN
SELECT id,score,crank,urank
BULK COLLECT INTO vid,vscore,vcrank,vurank
FROM test;
FORALL i IN 1 .. vid.COUNT
MERGE INTO final T
USING (SELECT vid (i) AS o_id,
vcrank (i) AS o_crank,
vurank (i) AS o_urank
vscore (i) AS o_score
FROM DUAL) S
ON (S.o_id = T.id)
WHEN MATCHED THEN
UPDATE SET T.crank = S.o_crank
WHERE T.crank <> S.o_crank;
UPDATE SET T.crank = S.o_crank
WHERE T.crank <> S.o_crank;
UPDATE SET T.crank = S.o_crank
WHERE T.crank <> S.o_crank;
UPDATE SET T.score = S.score
WHERE T.score <> S.score;
-- I tried the below case its not working either...
-- UPDATE SET T.crank = (CASE WHEN T.crank <> S.o_crank
-- THEN S.o_crank
-- END),
-- T.urank = (CASE WHEN T.urank <> S.o_urank
-- THEN S.o_urank
-- END);
COMMIT;
END;
/
I don't think you need the loop. I'm assuming your id's are primary keys and you didn't mean to repeat crank several times in your example.
Would something like this work?
Edit per Raj A's comment. This will only update rows where one of the other fields has changed. Note that this will not update rows where one is NULL and the other is not NULL.
MERGE INTO final T
USING ( SELECT id, score, crank, urank FROM test ) S
ON ( S.vid = T.id AND
( S.crank != T.crank OR S.score != T.score OR S.urank != T.urank ))
WHEN MATCHED SET crank = S.crank, score = S.score,
crank = S.crank, urank = S.urank
WHEN NOT MATCHED THEN INSERT
[... not sure what you want to do in this case ...]

How can I create a loop on an UPDATE statement that works until there is no row left to update?

Assume that I have thousands of rows to update.
And I plan to do the update iteratively; by only updating 1000 rows per iteration.
And I want to iterate until there are no rows left to update.
How can I run the T-SQL script below until there is no row to update?
-- TODO: Create a loop so that it exists when there is no ROW left to be updated;
-- how can I do it?
UPDATE tableToUpdate
SET IsVegetable = 1
WHERE Id IN
(SELECT TOP 1000 Id
FROM tableToUpdate
WHERE Date = '2011-07-23 14:00')
-- Loop ends
Try this loop
while 1 = 1
BEGIN
UPDATE top (1000) tableToUpdate
SET IsVegetable = 1
WHERE
Date = '2011-07-23 14:00'
AND IsNull(IsVegetable, 0) = 0
if ##ROWCOUNT < 1000 BREAK
END
Why ISNULL - because it is not clear - if the field IsVegetable is nullable or not, if not - then ISNULL not needed
When there no rows will left with IsVegetable <> 1 - the loop will quit because the ##ROWCOUNT will be = 0 or < 1000 (for the last iteration)