I want to leave always one record if table record count = 1 with SQL - sql

I can delete records with this SQL clause,
DELETE FROM TABLE WHERE ID = 2
I need to always leave one record if table count = 1 even if "ID=2". How can I do this?

Add a WHERE clause to ensure there's more than one row:
DELETE FROM TABLE
WHERE ID = 2
AND (SELECT COUNT(*) FROM TABLE) > 1

Untested, but something in the lines of this might work?
DELETE FROM TABLE WHERE ID = 2 LIMIT (SELECT COUNT(*)-1 FROM TABLE WHERE ID=2);
Maybe add in an if-statement to ensure count is above 1.

Simple way is to disallow any delete that empties the table
CREATE TRIGGER TRG_MyTable_D FOR DELETE
AS
IF NOT EXISTS (SELECT * FROM MyTable)
ROLLBACK TRAN
GO
More complex, what if you do this multirow delete that empties the table?
DELETE FROM TABLE WHERE ID BETWEEN 2 AND 5
so, randomly repopulate from what you just deleted
CREATE TRIGGER TRG_MyTable_D FOR DELETE
AS
IF NOT EXISTS (SELECT * FROM MyTable)
INSERT mytable (col2, col2, ..., coln)
SELECT TOP 1 col2, col2, ..., coln FROM INSERTED --ORDER BY ??
GO
However, the requirement is a bit dangerous and vague. In English, OK, "always have at least one row in the table", but in practice "which row?"

Related

How do I add Update statement to selected rows I am about to insert into a table with MS SQL query?

I have been busting my head for some time already and without any result.
Honestly I think that I need fresh eyes on this query.
I have written a query that deletes data from one table and puts it into another table. What I can't really figure out, is how to update one column for those rows I am moving, within the same query.
Here is how the query looks:
INSERT table1_archive
SELECT * FROM (
DELETE table
OUTPUT
DELETED.*
WHERE <condition1>
) AS RowsToMove;
What I want is to add also
UPDATE table1 SET <my_column> = "" WHERE <condition1>
Since it is the same condition and table for delete and update, I was thinking that it makes no sense to call two different queries to do some actions for exactly the same rows.
What I want is to clear data out of the <my_column> either before moving rows to table1_archive, or after doing so.
I guess my question is: How would I apply this update statement to the selected rows I am about to insert into the table1_archive?
ANSWER
This question becomes a little redundant as the UPDATE statement was not necessary to achieve what I wanted. I could just list all my columns in the SELECT statement and replace the <my_column> with NULL, or '''.
You can simply manipulate the column to be updated in the select statement.
INSERT INTO table1_archive
SELECT Col1,Col2...,"" AS <my_column> FROM (
DELETE table
OUTPUT
DELETED.*
WHERE <condition1>
) AS RowsToMove;
You can do this in a single statement - but it requires that you enumerate the columns.
Assuming that your tables have columns (col1, col2, col3, mycol), where mycol should be set to null when copied to the archive, you would write this as:
with del as (
delete ...
output deleted.*
where ...
)
insert into table1_archive (col1, col2, col3, mycol)
select col1, col2, col3, null
from del
and also you can try this solution
UPDATE example_table1 table1 ,
(SELECT
my_column1, my_column2
FROM example_table2
WHERE
table1.my_column3=<condition1>
) table2
SET
table1.my_column1 = table2.my_column1,
table1.my_column2 = table2.my_column2
where table1.ID = table2.ID
UPDATE table1 SET
table1.my_column1= table2.my_column1
FROM
example_table1 AS table1
INNER JOIN example_table2 AS table2
ON table1.ID = table2.ID
WHERE
table1.my_column2 = <condition1>

visual foxpro 9.0, how to find/get repeated records

I have a table with 2001233 records.
I can use 'Select distinct * from that_table' to get all records not repeated.
Maybe about 2001100 records.
How to get those 133 records into another table if I want to check the records disappeared after 'distinct'.
Another question is
When appending new records from one table to another table, how to check that the appended record is not already in the target table?
thanks for answering my question :)
It would be a hack and slow for 2+ million rows but you can do this:
Select Sys(2017,'',0,3) As crc, * ;
from myTable Into Cursor crsTemp ;
nofilter
Select * From crsTemp ;
where crc In ;
( Select crc From crsTemp;
having Count(*) > 1 ;
group By crc) ;
into Cursor crsDupes ;
nofilter
Select crsDupes
Browse
You should have used primary key from the start.
For your second question, I think it is best to use "insert into" rather than append. i.e.:
Insert into tableA ;
select * from tableB t1 ;
where not exists ( ;
select * from tableA t2 ;
where t1.field1 = t2.field1 and t1.field2 = t2.field2)
Here is another way to find the duplicate records:
Assuming that you don't keep deleted records hanging around...
select tableA
set deleted off
delete all
index on <key expression> to keyfield unique
set deleted on
recall all
browse for deleted
This process will delete all the records, and the recall statement will only apply to the indexed records, leaving the duplicates tagged as deleted.
Try this to check if there is a duplicate record.
SELECT colName, count(*) FROM tblName GROUP BY colName HAVING count(*) > 1

Delete subset of a table based on temp table

I have a table, say myTable. I also have a temp table, say myTableTemp, that contains the exact values I want to keep eliminate from myTable (myTable has more value than I need).
I was initially thinking I could drop myTable, and then rename myTableTemp to myTable`. However there are many FK contraints that I do not want to touch. In theory, my query would look like:
DELETE FROM myTable where in (myTableTemp);
At least logically that is how i think about it
EDIT: The temp table contains the data I want to DELETE from myTable
DELETE FROM myTable where in (myTableTemp);
Isn't the above backwards? Don't you want to keep all the values in myTableTemp?
I would do the following:
DELETE FROM myTable t1
WHERE NOT EXISTS ( SELECT 1 FROM myTableTemp t2
WHERE t2.primary_key = t1.primary_key );
Again, that's assuming that you want to keep everything in myTableTemp and delete everything in myTable that isn't in myTableTemp.
As an alternate solution to eliminate from myTable items present in myTableTemp:
DELETE FROM myTable
WHERE primary_key IN ( SELECT primary_key FROM myTableTemp )
;
It is usually believed that [NOT] EXISTS queries perform better than those using [NOT] IN. But it is not always that obvious.

Deleting at most one record for each unique tuple combination

I want to delete at most one record for each unique (columnA, columnB)-tuple in my following delete statement:
DELETE FROM tableA
WHERE columnA IN
(
--some subqueryA
)
AND columnB IN
(
--some subqueryB
)
How is this accomplished? Please only consider those statements that work when used against MSS 2000 (i.e., T-SQL 2000 syntax). I can do it with iterating through a temptable but I want to write it using only sets.
Example:
subqueryA returns 1
subqueryB returns 2,3
If the original table contained
(columnA, columnB, columnC)
5,2,5
1,2,34
1,2,45
1,3,86
Then
1,2,34
1,3,86
should be deleted. Each unique (columnA, columnB)-tuple will appear at most twice in tableA and each time I run my SQL statement I want to delete at most one of these unique combinations - never two.
If there is one record for a given unique (columnA, columnB)-tuple,
delete it.
If there are two records for a given unique (columnA,
columnB)-tuple, delete only one of them.
Delete tabA
from TableA tabA
Where tabA.columnC in (
select max(tabAA.columnC) from TableA tabAA
where tabAA.columnA in (1)
and tabAA.columnB in (2,3)
group by tabAA.columnA,tabAA.columnB
)
How often are you going to be running this that it matters whether you use temp tables or not? Maybe you should consider adding constraints to the table so you only have to do this once...
That said, in all honesty, the best way to do this for SQL Server 2000 is probably to use the #temp table as you're already doing. If you were trying to delete all but one of each dupe, then you could do something like:
insert the distinct rows into a separate table
delete all the rows from the old table
move the distinct rows back into the original table
I've also done things like copy the distinct rows into a new table, drop the old table, and rename the new table.
But this doesn't sound like the goal. Can you show the code you're currently using with the #temp table? I'm trying to envision how you're identifying the rows to keep, and maybe seeing your existing code will trigger something.
EDIT - now with better understood requirements, I can propose the following query. Please test it on a copy of the table first!
DELETE a
FROM dbo.TableA AS a
INNER JOIN
(
SELECT columnA, columnB, columnC = MIN(columnC)
FROM dbo.TableA
WHERE columnA IN
(
-- some subqueryA
SELECT 1
)
AND columnB IN
(
-- some subqueryB
SELECT 2 UNION SELECT 3
)
GROUP BY columnA, columnB
) AS x
ON a.columnA = x.columnA
AND a.columnB = x.columnB
AND a.columnC = x.columnC;
Note that this doesn't confirm that there are exactly one or two rows that match the grouping on columnA and columnB. Also note that if you run this twice it will delete the remaining row that still matches the subquery!

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.