Constraint on Column Based on Another Column Value - sql

I'm writing a sql constraint about values on columns based on some conditions in Oracle database. My table is like below.(assume id is auto increment, also 'alpha' and 'beta' columns are numbers)
id alpha beta
--------------------------
1 1 0
2 1 1
3 0 0
4 0 0
5 2 3
6 4 1
If alpha value in two rows are same, only one row can be inserted with beta value of 1. In other words, i shouldn't insert a row with (1,1) values because there is already a row with beta value of 1.(look at the row with id=2). Any value besides 1 can be inserted freely. I need a useful control about that situation.

You can use a function-based index:
create unique index xxx on t(a, case when b = 1 then -1 else id end)
Here is a db<>fiddle.

You can use unique index with condition-based column as follows:
create unique index u123
on your_table (alpha, case when beta = 1 then beta else id end)

Related

Query for checking duplicates and updating records

Hope youre doing well, need help with creating a SQL query (or multiple) to update the records in a table that its updated daily. The table has information about visits to stores from different users from the company that have to check if the store is in good conditions or not, it has 5 columns:
Key: String
Role: takes 3 values "rol1", "rol2" and "rol3" String
Date
User: String
Status: Boolean default is 1
The table is updated daily and need to check if there are duplicate values of the "key" attribute but with different roles values and change the "Status" to 0 for the old record and 1 (default) for the new record with duplicated key value based on the role, if the record has role value = "rol1" and a new record with "rol2" or "rol3" is added, need to keep both registers but change the status value to 0 to the previous record in the table with "rol1", thats in case the new record is "rol2" or "rol3". When the old record and the new record are both "rol1", need to change the status value based on the date, keep todays record with 1 and the older record to 0 (case that both records are "rol1").
When the existing record is "rol2" or "rol3" and the new record is "rol1", need to keep the old record with status 1 and the new to 0.
There can only be one true value for the status attribute with multiple keys.
12 FEB
KEY
ROLE
Col DATE
Col USER
STATUS
C KEY1
ROL1
DATE 1
TEXT
1
C KEY2
ROL2
DATE 3
TEXT
1
C KEY3
ROL1
DATE 3
TEXT
1
13 FEB
KEY
ROLE
Col DATE
Col USER
STATUS
C KEY1
ROL1
DATE 1
TEXT
0
C KEY2
ROL2
DATE 3
TEXT
1
C KEY3
ROL1
DATE 3
TEXT
0
C KEY1
ROL2
DATE 4
TEXT
1
C KEY3
ROL1
DATE 4
TEXT
1
C KEY2
ROL1
DATE 4
TEXT
0
It can be either a SQL query or maybe a Pandas script.
Thanks in advance guys!!
Was working on this query but soon realised it had to be different.
UPDATE table t
SET t.STATUS =
CASE WHEN t.ROLE = 'ROL2' THEN 1
WHEN EXISTS (
SELECT 1
FROM table t2
WHERE t.KEY = t2.KEY
AND t.ROLE = t2.ROLE
AND t2.ROLE = 'ROL1')
THEN 0
ELSE 1
END

How to set flag based on values in previous columns in same table ? (Oracle)

I'm creating a new table and carrying over several columns from a previous table. One of the new fields that I need to create is a flag that will have values 0 or 1 and value needs to be determined based on 6 previous fields in the table.
The 6 previous columns have preexisting values of 0 or 1 stored for each one. This new field needs to check whether any of the 6 columns have 1 and if so set the flag to 0. If there is 0 in all 6 fields then set itself to 1.
Hopefully this makes sense. How can I get this done in oracle? I assume a case statement and some sort of forloop?
You can use greatest() function: GREATEST
create table t_new
as
select
case when greatest(c1,c2,c3,c4,c5,c6)=1 -- at least one of them contains 1
then 0
else 1
end c_new
from t_old;
Or even shorter:
create table t_new
as
select
1-greatest(c1,c2,c3,c4,c5,c6) as c_new
from t_old;
In case of greatest = 1, (1-1)=0, otherwise (1-0)=1
You can use a virtual column with a case expression; something like:
flag number generated always as (
case when val_1 + val_2 + val_3 + val_4 + val_5 + val_6 = 0 then 1 else 0 end
) virtual
db<>fiddle
or the same thing with greatest() as #Sayan suggested.
Using a virtual column means the flag will be right for newly-inserted rows, and if any of the other values are updated; you won't have to recalculate or update the flag column manually.
I've assumed the other six columns can't be null and are constrained to only be 0 or 1, as the question suggests. If they can be null you can add nvl() or coalesce() to each term in the calculation.

Query to update missing columns

I have a table with 4 columns what I need to do is a query that set values to SP_REFSTART2, SP_REFSTOP2 if SP_REFSTART and SP_REFSTOP ARE NOT NULL else if the 2 first columns are null I want those values to fill the first two columns.
SP_REFSTART | SP_REFSTOP | SP_REFSTART2 | SP_REFSTOP2
1 1 null null
null null 1 1
Thats a sample of the table.
I want to get values 1 and 1 on the 3rd and 4th column since first 2 are not null
but if there were null I want 1 and 1 in the first 2 (row 2nd example).
UPDATE STORETRADELINES
SET SP_REFRSTART2 = CASE WHEN SP_REFRSTART IS NOT NULL THEN 1 ELSE NULL END
I did that my it will take me many lines since the columns are more than the example so I was thinking if there is any if to set multiple values.
Do you simply want coalesce()?
update STORETRADELINES
set SP_REFSTART = coalesce(SP_REFSTART, SP_REFSTART2),
SP_REFSTOP = coalesce(SP_REFSTOP, SP_REFSTOP2),
SP_REFSTART2 = coalesce(SP_REFSTART2, SP_REFSTART),
SP_REFSTOP2 = coalesce(SP_REFSTOP2, SP_REFSTOP)

how set value depending on a column value select statement t-sql. Like if 0 set 3, if less than 4 set 2 and so on

I have a column named photos, the photos column is of type tinyint and vary from 0 to 8. I want to apply a rank to the column when selecting data from the table. the values that want to get are: if the record has 4 or more photos, it receives a rank of 1. Because it is a fair amount of photos like 5, 6, 7 and 8. if the record has between 1 and 3 set a rank of 2, a medium rank, and if it has no photos set 3. with this and can sort the records based on the number of photos and make my appliation consistent.
Sure, you would just use a CASE statement in your ORDER BY. Something like this:
SELECT *
FROM Table
ORDER BY
CASE
WHEN photos >= 4 THEN 1
WHEN photos >= 1 THEN 2
ELSE 3
END
Or if you wanted it to be an actual column on the table, you could add it as a computed column:
ALTER TABLE mytable ADD rank AS
CASE
WHEN photos >= 4 THEN 1
WHEN photos >= 1 THEN 2
ELSE 3
END

Finding contiguous regions in a sorted MS Access query

I am a long time fan of Stack Overflow but I've come across a problem that I haven't found addressed yet and need some expert help.
I have a query that is sorted chronologically with a date-time compound key (unique, never deleted) and several pieces of data. What I want to know is if there is a way to find the start (or end) of a region where a value changes? I.E.
DateTime someVal1 someVal2 someVal3 target
1 3 4 A
1 2 4 A
1 3 4 A
1 2 4 B
1 2 5 B
1 2 5 A
and my query returns rows 1, 4 and 6. It finds the change in col 5 from A to B and then from B back to A? I have tried the find duplicates method and using min and max in the totals property however it gives me the first and last overall instead of the local max and min? Any similar problems?
I didn't see any purpose for the someVal1, someVal2, and someVal3 fields, so I left them out. I used an autonumber as the primary key instead of your date/time field; but this approach should also work with your date/time primary key. This is the data in my version of your table.
pkey_field target
1 A
2 A
3 A
4 B
5 B
6 A
I used a correlated subquery to find the previous pkey_field value for each row.
SELECT
m.pkey_field,
m.target,
(SELECT Max(pkey_field)
FROM YourTable
WHERE pkey_field < m.pkey_field)
AS prev_pkey_field
FROM YourTable AS m;
Then put that in a subquery which I joined to another copy of the base table.
SELECT
sub.pkey_field,
sub.target,
sub.prev_pkey_field,
prev.target AS prev_target
FROM
(SELECT
m.pkey_field,
m.target,
(SELECT Max(pkey_field)
FROM YourTable
WHERE pkey_field < m.pkey_field)
AS prev_pkey_field
FROM YourTable AS m) AS sub
LEFT JOIN YourTable AS prev
ON sub.prev_pkey_field = prev.pkey_field
WHERE
sub.prev_pkey_field Is Null
OR prev.target <> sub.target;
This is the output from that final query.
pkey_field target prev_pkey_field prev_target
1 A
4 B 3 A
6 A 5 B
Here is a first attempt,
SELECT t1.Row, t1.target
FROM t1 WHERE (((t1.target)<>NZ((SELECT TOP 1 t2.target FROM t1 AS t2 WHERE t2.DateTimeId<t1.DateTimeId ORDER BY t2.DateTimeId DESC),"X")));