How do I update n rows in a table? - sql

I need to update the first N rows in a table meeting a condition.
I know I can do an Update Top N... but the problem is that N is in a #variable.
UPDATE TOP #N SET ... doesn't work.
Is there a way to do this that I am just missing?
No specific table definitions here because it doesn't matter what the columns are.. If I can do it for a one column table I can do it for my table.

You need to use parens after TOP clause when you want to use a variable:
UPDATE TOP(#N) ...

WITH q AS
(
SELECT TOP (#r) *
FROM mytable
ORDER BY
col1
)
UPDATE q
SET co12 = #value
UPDATE TOP (#r) will work but it will update any #r rows in no particular order.
From the documentation:
The rows referenced in the TOP expression used with INSERT, UPDATE, or DELETE are not arranged in any order. TOP n returns n random rows.

Related

SQL Delete with group by clause

I want to write a SQL command that will delete all rows with a 0 as the last digit in a column, then a 1 as the last digit, a 2 as the last digit, and so on.
delete from BASE_TABLE where SET = 'ABCD'
This statement would delete 400,000+ rows at once and my service can't handle this.
to break it up I wanted to to say something like
delete from BASE_TABLE
where BASE_TABLE.SET = 'ABCD'
and BASE_TABLE.TAG LIKE '%0'
After I delete everything with LIKE '%0' I would want to delete everything with LIKE '%1', all the way through LIKE '%9'
Is this possible?
You can do that in a while loop. However, more typical approach would be:
delete top 10 percent from BASE_TABLE
where BASE_TABLE.SET = 'ABCD';
Or, just a fixed number that you can handle:
delete top 1000 from BASE_TABLE
where BASE_TABLE.SET = 'ABCD';
You can then put this in a loop:
declare #x int;
set #x = 1;
while #x > 0 begin
delete top 1000 from BASE_TABLE
where BASE_TABLE.SET = 'ABCD';
set #x = ##ROWCOUNT;
end;
Don't put the percent method in the while loop. It will keep going for a long time -- because the percent is based on the rows in the table at the time. I believe it will eventually delete the last row, but there will be a lot of iterations.
If system load is all you care about, just delete the top X rows. Here's the syntax
DELETE TOP (top_value) [ PERCENT ]
FROM table
[WHERE conditions];
You could apply an order by if you want to delete in a certain order. If you want the process to do all the work for you, stick it in a loop until the table is empty.
You need [0-9]% in where clause
This will delete all the rows begining with 0 to 9 and if you want ending with shift the % to the left as
%[0-9]

Oracle SQL, trying to get one value from a select/join to use to update one column in one table?

I have one table with the following columns:
T_RESOLVED_DATE
I_HOUSEHOLD_NUMBER
I_RESOLVED_SET_NUMBER
I_STATION_CODE
I_RESOLVED_START_MIN
I_DURATION
I_PERSON_NUMBER
I_COVIEW_DEMO_ID
Initially, I_COVIEW_DEMO_ID is set to null.
Then I have another table with the following columns:
T_RESOLVED_DATE
I_HOUSEHOLD_NUMBER
I_PERSON_NUMBER
I_AGE
T_GENDER
I_COVIEW_DEMO_ID
I am trying to update I_COVIEW_DEMO_ID in the first table by using the value of I_COVIEW_DEMO_ID in the second table where the T_RESOLVED_DATE, I_HOUSEHOLD_NUMBER, and I_PERSON_NUMBER are equal in both tables. The first table may contain multiple rows with the same DATE, HOUSEHOLD_NUMBER, and PERSON_NUMBER, because the rows can vary by the rest of the columns.
I have tried to do a select and a group by which seems to get me part way there, but I am getting a "single-row subquery returns more than one row" error when I try to update the columns in the first table. This is what I've tried, along with variations of it:
UPDATE
Table1
SET
I_COVIEW_DEMO_ID =
(SELECT
b.I_COVIEW_DEMO_ID
FROM Table1 a,
Table2 b
WHERE a.I_HOUSEHOLD_NUMBER = b.I_HOUSEHOLD_NUMBER AND
a.I_PERSON_NUMBER = b.I_PERSON_NUMBER AND
a.T_RESOLVED_DATE = b.T_RESOLVED_DATE
GROUP BY b.I_COVIEW_DEMO_ID);
Any suggestions?
I was able to get it to work using this statement:
MERGE INTO table1 a
USING
(
SELECT DISTINCT
T_RESOLVED_DATE,
I_HOUSEHOLD_NUMBER,
I_PERSON_NUMBER,
I_COVIEW_DEMO_ID
FROM
table2
) b
ON
(
a.T_RESOLVED_DATE = b.T_RESOLVED_DATE
AND a.I_HOUSEHOLD_NUMBER = b.I_HOUSEHOLD_NUMBER
AND a.I_PERSON_NUMBER = b.I_PERSON_NUMBER
) WHEN MATCHED THEN
UPDATE SET
a.I_COVIEW_DEMO_ID = b.I_COVIEW_DEMO_ID;
As per our discussion on the comments this would be a simple PLSQL block to do what you need. I'm doing direct from my head without test, so you may need to fix some sintaxe mistake.
BEGIN
FOR rs IN ( SELECT I_HOUSEHOLD_NUMBER,
I_PERSON_NUMBER,
I_COVIEW_DEMO_ID,
T_RESOLVED_DATE
FROM Table2 ) LOOP
UPDATE Table1
SET I_COVIEW_DEMO_ID = rs.I_COVIEW_DEMO_ID
WHERE I_PERSON_NUMBER = rs.I_PERSON_NUMBER
AND I_HOUSEHOLD_NUMBER = rs.I_HOUSEHOLD_NUMBER
AND T_RESOLVED_DATE = rs.T_RESOLVED_DATE;
END LOOP;
--commit after all updates, if there is many rows you should consider in
--making commits by blocks. Define a count and increment it whithin the for
--after some number of updates you commit and restart the counter
COMMIT;
END;

How to delete multiple rows in SQL where id = (x to y)

I am trying to run a SQL query to delete rows with id's 163 to 265 in a table
I tried this to delete less number of rows
DELETE FROM `table` WHERE id IN (264, 265)
But when it comes to delete 100's of rows at a time, Is there any query similar to above method
I am also trying to use this kind of query but failed to execute it
DELETE FROM `table` WHERE id IN (SELECT * FROM table WHERE id = )
Please tell me the query to do the above action...
If you need to delete based on a list, you can use IN:
DELETE FROM your_table
WHERE id IN (value1, value2, ...);
If you need to delete based on the result of a query, you can also use IN:
DELETE FROM your_table
WHERE id IN (select aColumn from ...);
(Notice that the subquery must return only one column)
If you need to delete based on a range of values, either you use BETWEEN or you use inequalities:
DELETE FROM your_table
WHERE id BETWEEN bottom_value AND top_value;
or
DELETE FROM your_table
WHERE id >= a_value AND id <= another_value;
You can use BETWEEN:
DELETE FROM table
where id between 163 and 265
Please try this:
DELETE FROM `table` WHERE id >=163 and id<= 265
SELECT * FROM your_table ORDER BY DESC
Get the range that you want to delete Ex: 3 to 10
DELETE FROM your_table
WHERE id BETWEEN 3 AND 10;
Make sure to add min value fist in BETWEEN Clause
CREATE PROC [dbo].[sp_DELETE_MULTI_ROW]
#CODE XML
,#ERRFLAG CHAR(1) = '0' OUTPUT
AS
SET NOCOUNT ON
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
DELETE tb_SampleTest
WHERE
CODE IN(
SELECT Item.value('.', 'VARCHAR(20)')
FROM #CODE.nodes('RecordList/ID') AS x(Item)
)
IF ##ROWCOUNT = 0
SET #ERRFLAG = 200
SET NOCOUNT OFF
Get string value delete
<RecordList>
<ID>1</ID>
<ID>2</ID>
</RecordList>

Update multiple rows using one query

Can I update multiple rows using one query?
How to union following queries:
UPDATE tablename SET col1='34355' WHERE id='2'
UPDATE tablename SET col1='152242' WHERE id='44'
You can use a virtual map table for this update.
update tablename
inner join (
select '34355' col1, '2' id union all
select '152242' col1, '44' id
) map on map.id = tablename.id
set tablename.col1 = map.col1
Using this pattern allows for easy expansion (just add rows to the map). It also allows MySQL to more predictably choose an index on tablename.id for the normal JOIN operation.
Can you? Sure. Should you? No way.
Think about the person looking at your code in five years. What's more readable, this:
UPDATE tablename SET col1='34355' WHERE id='2';
UPDATE tablename SET col1='152242' WHERE id='44';
or this (The Scrum Meister's answer):
UPDATE tablename SET col1 = IF(id='2', '34355','152242') WHERE id='2' OR id='44';
The second one is shorter, but it's a challenge to figure out exactly what it's doing. If you're worried about race conditions, make it a single transaction (in most modern DBMS):
BEGIN;
UPDATE tablename SET col1='34355' WHERE id='2';
UPDATE tablename SET col1='152242' WHERE id='44';
COMMIT;
That way you can be guaranteed no other query will run when row 2 is updated but row 44 is not.
You can use a OR clause combined with the IF() function (or CASE WHEN... for other RDBMS)
UPDATE tablename SET col1 = IF(id='2', '34355','152242')
WHERE id='2' OR id='44'
Generally the only way you can update multiple rows in a single query is if your where clause matches multiple rows... and then every row will have the same values set.
Past that you can do funky stuff with expressions in your set clauses, but generally it's cleaner to do multiple queries, unless there's a very specific reason you can't.

T-SQL cursor and update

I use a cursor to iterate through quite a big table. For each row I check if value from one column exists in other.
If the value exists, I would like to increase value column in that other table.
If not, I would like to insert there new row with value set to 1.
I check "if exists" by:
IF (SELECT COUNT(*) FROM otherTabe WHERE... > 1)
BEGIN
...
END
ELSE
BEGIN
...
END
I don't know how to get that row which was found and update value. I don't want to make another select.
How can I do this efficiently?
I assume that the method of checking described above isn't good for this case.
Depending on the size of your data and the actual condition, you have two basic approaches:
1) use MERGE
MERGE TOP (...) INTO table1
USING table2 ON table1.column = table2.column
WHEN MATCHED
THEN UPDATE SET table1.counter += 1
WHEN NOT MATCHED SOURCE
THEN INSERT (...) VALUES (...);
the TOP is needed because when you're doing a huge update like this (you mention the table is 'big', big is relative, but lets assume truly big, +100MM rows) you have to batch the updates, otherwise you'll overwhelm the transaction log with one single gigantic transaction.
2) use a cursor, as you are trying. Your original question can be easily solved, simply always update and then check the count of rows updated:
UPDATE table
SET column += 1
WHERE ...;
IF ##ROW_COUNT = 0
BEGIN
-- no match, insert new value
INSERT INTO (...) VALUES (...);
END
Note that this approach is dangerous though because of race conditions: there is nothing to prevent another thread from inserting the value concurrently, so you may end up with either duplicates or a constraint violation error (preferably the latter...).
This is just psuedo code because I have no idea of your table structure but I think you will understand... basically Update the columns you want then Insert the columns you need. A Cursor operation sounds unnecessary.
Update OtherTable
Set ColumnToIncrease = ColumnToIncrease + 1
FROM CurrentTable Where ColumnToCheckValue is not null
Insert Into OtherTable (ColumnToIncrease, Field1, Field2,...)
SELECT
1,
?
?
FROM CurrentTable Where ColumnToCheckValue is not null
Without a sample, I think this is the best I can do. Bottom line: you don't need a cursor. UPDATE where a match exists (INNER JOIN) and INSERT where one does not.
UPDATE otherTable
SET IncrementingColumn = IncrementingColumn + 1
FROM thisTable INNER JOIN otherTable ON thisTable.ID = otherTable.ID
INSERT INTO otherTable
(
ID
, IncrementingColumn
)
SELECT ID, 1
FROM thisTable
WHERE NOT EXISTS (SELECT *
FROM otherTable
WHERE thisTable.ID = otherTable.ID)
I think you'd be better off using a view for this -- then it's always up to date, no risk of mistakenly double/triple/etc counting:
CREATE VIEW vw_value_count AS
SELECT st.value,
COUNT(*) AS numValue
FROM SOME_TABLE st
GROUP BY st.value
But if you still want to use the INSERT/UPDATE approach:
IF EXISTS(SELECT NULL
FROM SOMETABLE WHERE ... > 1)
BEGIN
UPDATE TABLE
SET count = count + 1
WHERE value = #value
END
ELSE
BEGIN
INSERT INTO TABLE
(value, count)
VALUES
(#value, 1)
END
What about Update statement with inner join to perform +1, and Insert selected rows that do not exist in the first table.
Provide the tables schema and the columns you want to check and update so I can help.
Regards.