SQL Delete with group by clause - sql

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]

Related

Show results from query if results exist without running the same query again

I want to have a stored procedure that will take one SerialNumber nvarchar as it's input and check several databases to see if that serial number exists and if it does exist then return the result of the query, otherwise move onto the next database and do the same thing until all databases have been checked.
Current pseudocode:
IF(exists(select top 1 * from Server1.Database1.Table where num = #SerialNumberInput))
BEGIN
select top 1 * from Server1.Database1.Table where num = #SerialNumberInput
END ELSE
IF(exists(select top 1 * from Server2.Database2.Table where num = #SerialNumberInput))
BEGIN
select top 1 * from Server2.Database2.Table where num = #SerialNumberInput
END ELSE
--Server3.Database3
--Server4.Database4
--etc...
But I don't like all this query repetition and I don't like how I'm having to make a call to the server twice by calling the same query twice. I could save the result to a table variable and just check that but that feels hacky.
Too long to comment.
But I don't like all this query repetition
Me neither, but for this case, it's the cleanest method or most readable IMHO.
I don't like how I'm having to make a call to the server twice by
calling the same query twice.
You aren't, at least not exactly. EXISTS returns a BOOLEAN value so as long as there is an INDEX on your predicate, it should be pretty fast. The second query, where you are returning the first row with all of the columns would be slightly slower. Also, you don't need top 1 * in the EXISTS unless you just like that. You can use SELECT 1 or anything since the result is BOOLEAN.
Another thing is you are using TOP without and ORDER BY which means you don't care what row is returned, and are OK with that row being different (potentially) each time you execute this. More on that in this blog.
If you really want to not use EXISTS, you can break this up using ##ROWCOUNT.
select top 1 * from Server1.Database1.Table where num = #SerialNumberInput
if ##ROWCOUNT = 1
return
else
select top 1 * from Server2.Database2.Table where num = #SerialNumberInput
if ##ROWCOUNT = 1
return
else
...
Or, if the schema is the same and you don't want NULL datasets... something like you said with the table variable.
create table #Temp(...)
insert into #Temp
select top 1 * from Server1.Database1.Table where num = #SerialNumberInput
if ##ROWCOUNT = 1
select * from #Temp
return
else
insert into #Temp
select top 1 * from Server2.Database2.Table where num = #SerialNumberInput
if ##ROWCOUNT = 1
select * from #Temp
return
else
...
Since you are only inserting a single row, it'd be pretty quick. Larger datasets would naturally take longer.

SQL SELECT statement within an IF statement

I have a trigger in SQL Server that needs to check on an update the number of rows with a value between a certain amount and do something accordingly. My current code is something like this:
IF EXISTS(SELECT COUNT(id) as NumberOfRows
FROM database
WHERE id = 3 AND value <= 20 and value > 2
GROUP BY id
HAVING COUNT(id) > 18)
-- if true, do something
From what I can tell, the select statement should find the number of rows with a value between 2 and 20 and if there are more than 18 rows, the EXISTS function should return 1 and the query will execute the code within the IF statement.
However, what is happening is that it is always executing the code within the IF statement regardless of the number of rows with a value between 2 and 20.
Any ideas on why this might be? I can post more complete code if it might help.
The reason is that the Exists function is checking the result of the sub-query for existing - are there any rows or not. And, as you return the COUNT, it'll never be not-existing - COUNT returns 0 if there are no rows presented in database.
Try to store the resulting count in a local variable, like in this question:
Using IF ELSE statement based on Count to execute different Insert statements
DECLARE #retVal int
SELECT #retVal = COUNT(*)
FROM TABLE
WHERE COLUMN = 'Some Value'
IF (#retVal > 0)
BEGIN
--INSERT SOMETHING
END
ELSE
BEGIN
--INSERT SOMETHING ELSE
END
I would do it like so (single line):
IF ((SELECT COUNT(id) FROM table WHERE ....)>18) BEGIN
...do something
You can even do between in a single line
IF ((SELECT COUNT(id) FROM table WHERE ....)between 2 and 20) BEGIN
...do something
END
Your subquery is looking for matches in the entire table. It does not limit the results only to those that are related to the rows affected by the update. Therefore, if the table already has rows matching your condition, the condition will be true on any update that affects other rows.
In order to count only the relevant rows, you should either join the database table to the inserted pseudo-table or use just the inserted table (there is not enough information in your question to be sure which is better).

insert a random number into each row in table

I currently have an oracle table (lovalarm) containing around 600,000 rows. I need to be able to run a query which will cycle through each row and update a field (lovsiteid) to a random number between 14300 and 17300.
So far I have:
update lovalarm
set lovsiteid = (select TRUNC(dbms_random.value(14300,17300)) FROM dual)
Sadly this picks a random number and then updates all rows with the same number which isn't exactly what I'm after!
Can anyone point me in the right direction?
Many thanks,
Cap
Just not use subquery:
update lovalarm
set lovsiteid = TRUNC(dbms_random.value(14300,17300))
Try this:
update lovalarm set lovsiteid = (select FLOOR(RAND() * (17300 - 14300) + 14300))
works in MySQL

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.

How do I update n rows in a table?

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.