Update one row based on value in another row - sql

I have an table with following columns and values:
SubscriptionName, Status, Ignore
Project Plan 3 for faculty, Enabled, Null
Project Plan 3 for faculty, Suspended, Null
How can I update the Ignore column to True for the suspended record, if there are 2 entries with the same subscriptionName and the other record has the value Enabled in Status

In SQL Server, you can do this with window functions and an updatable CTE:
with cte (
select
t.*,
max(case when status = 'Enabled' then 1 end)
over(partition by SubscriptionName) has_enabled
from mytable t
)
update cte
set ignore = 'True'
where status = 'Suspended' and has_enabled = 1
The conditional window max() checks if another row exists with the same SubscriptionName and status 'Enabled'.
Or you can use exists:
update t
set ignore = 'True'
from mytable t
where
status = 'Suspended'
and exists (
select 1
from mytable t1
where t1.SubscriptionName = t.SubscriptionName and t1.status = 'Enabled'
)

Related

Finding rows in SQL where changes but only certain changes while keeping others

I have this scenario where I want each occurrence of an active row to bring back that row in my result set and also inactive if there is only 1 inactive record for that IDENTIFIER and also if there are more than 1 active also show those. I've used Row_Number function and then in another query show where the row = '1' but if I do that row 1s only come back and then I lose some of my desired results. To restate my issue is I want all active records to come back and only inactive where IDENTIFIER is unique. The row that is bold should not be shown in the results.
1 has 1 active record in the DB.
2 has 2 active and 1 inactive records.
3 has no active records.
4 has only 2 active records, no inactive.
You can use a windowed conditional count, this has the benfit of only scanning the table once
SELECT
t.IDENTIFIER,
t.DB_ID,
t.Status
FROM (
SELECT *,
HasActive = COUNT(CASE WHEN t.Status = 'Active' THEN 1 END) OVER (PARTITION BY t.IDENTIFIER)
FROM YourTable t
) t
WHERE t.Status = 'Active' OR t.HasActive = 0;
One way to do this is with NOT EXISTS:
SELECT t1.*
FROM tablename t1
WHERE t1.Status = 'Active'
OR NOT EXISTS (
SELECT 1
FROM tablename t2
WHERE t2.identifier = t1.identifier AND t2.db_id <> t1.db_id
);
I assume that the column db_id is unique, at least for the same identifier.
If I understood you correctly, this is my variant.
select IDENTIFIER, [DB_ID], [Status]
from Tab
where [Status]='Active'
union
select IDENTIFIER, [DB_ID], [Status]
from Tab as t
where [Status]='Inactive' And 1=(select Count(*) from Tab where
IDENTIFIER=t.IDENTIFIER)
Order by IDENTIFIER, [DB_ID]
you can do it like this, because (rank=1 and Status=Inactive) only if there are no active rows for a particular Identifier
select * from (
select *,
DENSE_RANK() OVER (PARTITION BY identifier order by status) AS rank
from some_table
)
where rank=1 or status = 'Active'

SQL Query to get the workorders which skipped some lines processing

As I am not expert in writing the SQL queries so want for help.
I have the below given dataset
I need to write a query to get all the workorderid's which have skipped the process and moved to next seq_no , ex:
workorderid = AW1 which has a line "open" for seq_no=30 and went ahead to "complete" for seq_no = 40
The final result set should look like below
Workorderid
-----------
AW1
AW3
One method simply uses conditional aggregation:
select workorderid
from t
group by workorderid
having (max(case when status = 'Open' then seq_no end) <
max(case when status = 'Complete' then seq_no end)
)
That is, is there a 'Complete' after the last 'Open', based on seq_no.
Here is another one using EXISTS:
SELECT t1.workorderid
FROM t t1
WHERE t1.status = 'Open'
AND EXISTS
(SELECT 1
FROM t t2
WHERE t2.workorderid = t1.workorderid
AND t2.seq_no > t1.seq_no
AND t2.status = 'Complete')

SQL Server : do not Select all if true

I have these columns
Id Status
----------
1 pass
1 fail
2 pass
3 pass
How do I select all that only have a status of pass but if the Id has at least one fail it will not be selected as well.
If same id can have multiple passes
SELECT id
from table
WHERE status = 'pass'
and id NOT IN (SELECT id FROM table WHERE status = 'fail')
You need to use GROUP BY & HAVING clause
SELECT Id
FROM yourtable
GROUP BY Id
HAVING Sum(case when status ='pass' then 1 else 0 end) = count(status)
HAVING clause can be changed to
HAVING Count(case when status ='pass' then 1 end) = count(status)
I just hate chatty case statement, so
SELECT Id
FROM table1
GROUP BY Id
HAVING COUNT(DISTINCT [Status]) = 1 AND MIN([Status]) = 'pass'
or
SELECT Id
FROM table1
GROUP BY Id
HAVING COUNT(NULLIF([Status], 'fail')) = 1 AND COUNT(NULLIF([Status], 'pass')) = 0
The second query only works when status has two values 'pass' and 'fail'.

Replace NULL with values

Here is my challenge:
I have a log table which every time a record is changed adds a new record but puts a NULL value for each non-changed value in each record. In other words only the changed value is set, the rest unchanged fields in each row simply has a NULL value.
Now I would like to replace each NULL value with the value above it that is NOT a NULL value like below:
Source table: Task_log
ID Owner Status Flag
1 Bob Registrar T
2 Sue NULL NULL
3 NULL NULL F
4 Frank Admission T
5 NULL NULL F
6 NULL NULL T
Desired output table: Task_log
ID Owner Status Flag
1 Bob Registrar T
2 Sue Registrar T
3 Sue Registrar F
4 Frank Admission T
5 Frank Admission F
6 Frank Admission T
How do I write a query which will generate the desired output table?
One the new windowed function of SQLServer 2012 is FIRST_VALUE, wich have quite a direct name, it can be partitioned through the OVER clause, before using it is necessary to divide every column in data block, a block for a column begin when a value is found.
With Block As (
Select ID
, Owner
, OBlockID = SUM(Case When Owner Is Null Then 0 Else 1 End)
OVER (ORDER BY ID)
, Status
, SBlockID = SUM(Case When Status Is Null Then 0 Else 1 End)
OVER (ORDER BY ID)
, Flag
, FBlockID = SUM(Case When Flag Is Null Then 0 Else 1 End)
OVER (ORDER BY ID)
From Task_log
)
Select ID
, Owner = FIRST_VALUE(Owner) OVER (PARTITION BY OBlockID ORDER BY ID)
, Status = FIRST_VALUE(Status) OVER (PARTITION BY SBlockID ORDER BY ID)
, Flag = FIRST_VALUE(Flag) OVER (PARTITION BY FBlockID ORDER BY ID)
FROM Block
SQLFiddle demo
The UPDATE query is easily derived
As I mentioned in my comment, I would try to fix the process that is creating the records rather than fixing the junk data. If that is not an option, the code below should get you pointed in the right direction.
UPDATE t1
set t1.owner = COALESCE(t1.owner, t2.owner),
t1.Status = COALESCE(t1.status, t2.status),
t1.Flag = COALESCE(t1.flag, t2.flag)
FROM Task_log as t1
INNER JOIN Task_log as t2
ON t1.id = (t1.id + 1)
where t1.owner is null
OR t1.status is null
OR t1.flag is null
I can think of several approaches.
You could use a combination of COALESCE with an array aggregate function. Unfortunately it doesn't look like SQL Server supports array_agg natively (although some nice people have developed some workarounds).
You could also use a subselect for each column.
SELECT id,
(SELECT TOP 1 FROM (SELECT owner FROM ... WHERE id = outer_id AND owner IS NOT NULL order by ID desc )) AS owner,
-- other columns
You could probably do something with window functions, too.
A vanilla solution would be:
select id
, owner
, coalesce(owner, ( select owner from t t2
where id = (select max(id) from t t3
where id < t1.id and owner is not null))
) as new_owner
, flag
, coalesce(flag, ( select flag from t t2
where id = (select max(id) from t t3
where id < t1.id and flag is not null))
) as new_flag
from t t1
Rather inefficient, but should work on most DBMS

oracle sql - compare row 1 and two, show results in row 3?

My table holds type 2 change history; so if recordID 123 has a change made by a user, then 'latest_effective' gets set to FALSE and the newly created record with recordID 123 has 'latest_effective' set to TRUE.
I'm looking for a query that returns the new record & the record that was just changed with a third row that is TRUE if there is a change in that column and FALSE if not.
select RecordID,1 latest_effective,Column1,Column2, etc...
from YourTable
where latest_effective = 1
union all
select RecordID,0 latest_effective,Column1,Column2, etc...
from YourTable
where latest_effective = 0
union all
select t.RecordID,-1 latest_effective,
case t.Column1 when f.Column1 then 'FALSE' else 'TRUE' end AS Column1
case t.Column2 when f.Column2 then 'FALSE' else 'TRUE' end AS Column2, etc...
from YourTable AS t
inner join YourTable AS f
on f.RecordID = t.RecordID
and f.latest_effective = 0
where t.latest_effective = 1
order by RecordID,latest_effective desc