Suggestion for sql scenario - sql

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)

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.

SQL Insert when not exist Update if exist with mutliple rows in Target-Table

I have two tables, where table A has to be Updated or insert a row base on existing. I tried this by using JOINS EXCEPT and MERGE statement but I have one problem i can't solve. so here is an example :
Table A (Attribut-Table)
attr | attrValue | prodID
--------------------------
4 | 2 | 1
--------------------------
3 | 10 | 2
--------------------------
1 | 7 | 2
--------------------------
3 | 10 | 3
--------------------------
6 | 9 | 3
--------------------------
1 | 4 | 3
--------------------------
Table P(Product-Table)
prodID | stock |
------------------
1 | 1
------------------
2 | 0
------------------
3 | 1
------------------
4 | 1
------------------
Now what i would like to do the following in SQL:
All products, that has Stock > 0 should have an entry in Table A with attr = 6 and attrValue = 9
All products, that has Stock < 1 should have an entry in Table A with attr = 6 and attrValue = 8
i need a SQL Query to do that because my problem is that there are multiple entries for a prodID in Table A
That is what i am thinking of:
Fist check if any entry for the prodID(in Table B) exist in Table A, if not INSERT INTO Table A ( attr=6 and, attrValue = 8/9 (depends on Stock), prodID
If there is already an entry for the prodID in Table A with the attr = 6, then Update this row and set attrValue to 8/9 (depending on stock)
so I am looking for a translation of "my thoughts" to a sqlQuery
thanks for helping.
(using: SQL SERVER Express 2012 and HEIDI SQL for management)
Since your "attr 6" row is 100 % derivable from the state of the P table, it is a poor idea to store that row redundantly in A.
This is better :
(1) Define a first view ATTR6_FOR_P as SELECT prodID, 6 as attr, CASE (...) as attrValue from P. The CASE expression chooses the value 8 or 9 according to stock value in P.
(2) Define a second view A_EXT as A UNION ATTR6_FOR_P. (***)
Now changes in stock will always immediately be reflected in A_EXT without having to update explicitly.
(***) but beware of column ordering because SQL UNION does not match columns by name but by ordinal position instead.

Update column in one table for a user based on count of required records in another table for same user without 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.
This is an update for my earlier question. Thanks for the help.
Here's a solution for PostgreSQL:
update TableA
set IsCorrect =
case when
string_to_array(Required, ';') <#
(select array_agg(PPName)
from TableB
where TableA.UserID = TableB.UserID)
then 1
else 0
end;
You can also see it live on SQL Fiddle.
use sub-query and aggregate function and then case when for conditional update
update TableA A
set A.IsCorrect= case when T.cnt>=3 then 1 else 0 end
inner join
(
select B.UserID ,count(*) as cnt from TableB as B
group by UserID
) as T
on A.userid=T.UserID

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);

Renumber duplicates to make them unique

Table "public.t"
Column | Type | Modifiers
--------+---------+-----------
code | text |
grid | integer |
The codigo column, although of type text, has a numeric sequence which has
duplicates. The grid column is a unique sequence.
select * from t order by grid;
code | grid
------+------
1 | 1
1 | 2
1 | 3
2 | 4
2 | 5
2 | 6
3 | 7
The goal is to eliminate the duplicates in the code column to make it unique. The result should be similar to:
code | grid
------+------
1 | 1
6 | 2
4 | 3
2 | 4
7 | 5
5 | 6
3 | 7
The version is 8.2 (no window functions).
create table t (code text, grid integer);
insert into t values
('1',1),
('1',2),
('1',3),
('2',4),
('2',6),
('3',7),
('2',5);
This is the solution that worked.
drop sequence if exists s;
create temporary sequence s;
select setval('s', (select max(cast(code as integer)) m from t));
update t
set code = i
from (
select code, grid, nextval('s') i
from (
select code, max(grid) grid
from t
group by code
having count(*) > 1
order by grid
) q
) s
where
t.code = s.code
and
t.grid = s.grid
The problem with it is that the update command must be repeated until there are no more duplicates. It is just a "it is not perfect" problem as it is a one time operation only.
Export (and remove) everything but code column (maybe you could subquery for export, and remove just duplicated row). Make code primary with something such auto increment behaviour and reimport everything. code column should be automatically generated.