SQL UPDATE issues - sql

I'm currently working on an application where an UPDATE query is performed multiple times in a row on a single table and I've stumbled into a problem.
If UPDATE query ends up swapping two rows, e.g. UPDATING 1 -> 2 and then 2 -> 1
The following happens
original | 1 -> 2 | 2 -> 1 | what i want
1 | 2 | 1 | 2
2 | 2 | 1 | 1
3 | 3 | 3 | 3
4 | 4 | 4 | 4
There are no other columns which can be used to further differentiate the tuples consistently.
Is there a way to achieve 'what i want' without restructuring the table/database? One solution I could think of is to first delete all the rows and insert the updated ones instead (this is satisfactory implementation wise) but I'd like to know whether it's doable with an UPDATE query.

Either make this one update:
update mytable
set col = case when col = 1 then 2 else 1 end
where col in (1,2);
Or three updates (by using an "impossible" value, i.e. a value that is not used in the column):
update mytable set col = -1 where col = 1;
update mytable set col = 1 where col = 2;
update mytable set col = 2 where col = -1;

u may select the ids first and put them on a two separate lists or variables to store ids then update based on that previously selected ids ?

Related

SQL - Set column value to the SUM of all references

I want to have the column "CurrentCapacity" to be the SUM of all references specific column.
Lets say there are three rows in SecTable which all have FirstTableID = 1. Size values are 1, 1 and 3.
The row in FirstTable which have ID = 1 should now have a value of 5 in the CurrentCapacity column.
How can I make this and how to do automatically on insert, update and delete?
Thanks!
FirstTable
+----+-------------+-------------------------+
| ID | MaxCapacity | CurrentCapacity |
+----+-------------+-------------------------+
| 1 | 5 | 0 (desired result = 5) |
+----+-------------+-------------------------+
| 2 | 5 | 0 |
+----+-------------+-------------------------+
| 3 | 5 | 0 |
+----+-------------+-------------------------+
SecTable
+----+-------------------+------+
| ID | FirstTableID (FK) | Size |
+----+-------------------+------+
| 1 | 1 | 2 |
+----+-------------------+------+
| 2 | 1 | 3 |
+----+-------------------+------+
In general, a view is a better solution than trying to keep a calculated column up-to-date. For your example, you could use this:
CREATE VIEW capacity AS
SELECT f.ID, f.MaxCapacity, COALESCE(SUM(s.Size), 0) AS CurrentCapacity
FROM FirstTable f
LEFT JOIN SecTable s ON s.FirstTableID = f.ID
GROUP BY f.ID, f.MaxCapacity
Then you can simply
SELECT *
FROM capacity
to get the results you desire. For your sample data:
ID MaxCapacity CurrentCapacity
1 5 5
2 5 0
3 5 0
Demo on SQLFiddle
Got this question to work with this trigger:
CREATE TRIGGER UpdateCurrentCapacity
ON SecTable
AFTER INSERT, UPDATE, DELETE
AS
BEGIN
SET NOCOUNT ON
DECLARE #Iteration INT
SET #Iteration = 1
WHILE #Iteration <= 100
BEGIN
UPDATE FirstTable SET FirstTable.CurrentCapacity = (SELECT COALESCE(SUM(SecTable.Size),0) FROM SecTable WHERE FirstTableID = #Iteration) WHERE ID = #Iteration;
SET #Iteration = #Iteration + 1
END
END
GO
Personally, I would not use a trigger either or store CurrentCapacity as a value since it breaks Normalization rules for database design. You have a relation and can already get the results by creating a view or setting CurrentCapacity to a calculated column.
Your view can look like this:
SELECT Id, MaxCapacity, ISNULL(O.SumSize,0) AS CurrentCapacity
FROM dbo.FirstTable FT
OUTER APPLY
(
SELECT ST.FirstTableId, SUM(ST.Size) as SumSize FROM SecTable ST
WHERE ST.FirstTableId = FT.Id
GROUP BY ST.FirstTableId
) O
Sure, you could fire a proc every time a row is updated/inserted or deleted in the second table and recalculate the column, but you might as well calculate it on the fly. If it's not required to have the column accurate, you can have a job update the values every X hours. You could combine this with your view to have both a "live" and "cached" version of the capacity data.

Suggestion for sql scenario

I am having two columns in table
InventoryId | RevisionId
-------------------------
1 | 1
2 | 1
2 | 2
2 | 2
3 | 1
3 | 2
3 | 3
3 | 3
but from now on I want to prevent following records
2 | 2
2 | 2
3 | 3
3 | 3
So I thought to create a unique index on these two columns
but the table having so much existing data. So anything we can do this situation.
Any suggestion?
you can use a trigger to prevent new rows being added with duplicate values
look at this example
create trigger TR_UI_YourTable on YourTable
for update, insert as
begin
set nocount on
if exists (select 1 from inserted i where i.InventoryId = i.RevisionId)
begin
;throw 99001, 'no dupes allowed anymore...', 1
end
end
A better solution would be to move the duplicates to a seperate table for history, and then add a check constraint on these 2 columns
EDIT
you could do it by an check constraint like this
alter table yourtable
add constraint chk_dupes
check ((InventoryId <> RevisionId) or (id <= 12345))
where 12345 is the highest value of the column id now.
You will have to test it a bit if it works on all situations.
Also, it will only work if all new rows have a value in id that is larger then the current highest value (12345 in my example)

Ntile function not dividing groups evenly when being ran from stored procedure

I have the following code inside a stored procedure.
select
ID,NTILE(2) OVER (Partition by GroupID order by newID()) as RandomSplit
into #TempSplit
from TableA
where IsUpdated = 1
Update a
set a.SplitColumn = CASE WHEN b.RandomSplit = 1 THEN 'A'
WHEN b.RandomSplit = 2 THEN 'B'
END
from Table A a
inner join #TempSplit b
on a.ID = b.ID and a.IsUpdated = 1
This code works as expected and produces the data table below.
GroupID SplitColumn
1 | A
1 | A
1 | B
1 | B
2 | A
3 | A
3 | B
However,when I execute this code from the stored procedure I get the following results
GroupID SplitColumn
1 | A
1 | A
1 | A
1 | B
2 | A
3 | A
3 | B
This is sample data but basically what is happening is that when I execute from the stored procedure the groups are not distributed evenly(in the real data the variation is by thousands rather than just one). Not sure what is exactly causing this behavior since again if I execute the code manually it comes up with the correct behavior.
Also I know this is a small sample of what is happening, but its also not happening for all GroupIDs. Meaning GroupID = 3 always gets split correctly into two even groups, while say GroupID = 1 always gets wrongly split.
You are creating #TempSplit only for the rows where IsUpdated = 1.
However, you are joining back to all the values. If id is duplicated in TableA, then you would get results as you see.

Update column in one table for a user based on count of records in another table for same user without using cursor

I have 2 tables A and B. I need to update a column in table A for all userid's based on the count of records that userid has in another table based on defined rules. If count of records in another table is 3 and is required for that userID, then mark IsCorrect as 1 else 0, if count is 2 and required is 5 then IsCorrect as 0 For e.g. Below is what I am trying to achieve
Table A
UserID | Required | IsCorrect
----------------------------------
1 | SO;GO;PE | 1
2 | SO;GO;PE;PR | 0
3 | SO;GO;PE | 1
Table B
UserID | PPName
-----------------------
1 | SO
1 | GO
1 | PE
2 | SO
2 | GO
3 | SO
3 | GO
3 | PE
I tried using Update in table joining another table, but cannot up with one. Also, do not want to use cursors, because of its overhead. I know I will have to create a stored Procedure for it for the rules, but how to pass the userID's to it without cursor is what am i am looking for.
Thanks for the help. Apologies for not formatting the table correctly :)
update A
set IsCorrect = case
when Required <= (select count(*) from B where b.UserID = A.UserID)
then 'Y' -- or 0, or whatever sense is appropriate
else 'N'
end
THIS ANSWERS THE ORIGINAL QUESTION.
Hmmm, you can use a correlated subquery and some case logic:
update a
set iscorrect = (case when required <=
(select count(*) from b where b.userid = a.userid)
then 1 else 0
end);

Sqlite : Loop through rows and match, break when distinct row encountered

I want to compare two tables A and B, row by row on the basis of a column name.As soon as I encounter a distinct row I want to break.
I want to do this using a query, something like this :
select case
when ( compare row 1 of A and B
if same continue with row+1
else break
)
if all same, then 1
else 0
end
as result
I am not sure how to loop through rows and break? Is it even possible in sqlite?
EDIT
Table looks like this
-------------------------- -----------------------------------
id | name id | name
-------------------------- -----------------------------------
1 | A 1 | A (same)
2 | C 2 | C (same)
3 | B 3 | Z (different break)
4 | K
Both tables have same structure. I want to just compare the names row by row, to see whether there is any order difference.