How to select rows where fields change - sql

I have data similar to the following:
Version
ID
Reviewer
Action
Decision_Type
Review_start_time
Review_end_time
3
ABC123
Bob
Approve
Reactive
2021-03-31 16:49:16.0
2021-03-31 20:10:06.0
2
ABC123
Bob
Block
Pre-Publish
2021-03-31 16:49:16.0
2021-03-31 20:09:29.0
1
ABC123
System
InProgress
Pre-Publish
2021-03-31 16:49:16.0
2021-03-31 17:50:52.0
I need to be able to query IDs where:
The reviewer was Bob
Changes from Action = Block & Decision_Type = Pre-Publish to Action = Approve & Decision_Type = Reactive
(NOTE: the version numbers will change depending on how many reviews were completed, this is just an example)

You can join the table to itself on the ID and Reviewer and test for the presence of both kinds of records:
Select b.id, b.reviewer
From MyTable b Inner Join MyTable a On b.id=a.id and b.reviewer=a.reviewer
Where b.Action = 'Block' and b.Decision_Type = 'Pre-Publish'
and a.Action = 'Approve' and a.Decision_Type = 'Reactive'
and b.review_end_time<a.review-end-time
I added a requirement to the Where to ensure Approve came after Block using review end. Based on your comment it seems you cannot be certain that the Approve' comes directly after, just somewhere after.

Related

Select columns value based on logic applied on the rows

It is a self-join I need, but I'm having difficulty with this problem and I hope someone can help me.
I have a table with MAT_CODE, MATERIAL and VENDOR and I am trying to generate a new column with NEW_MATCODE as per the below scenario.
Sample Data :
NEW_MATCODE MAT_CODE MATERIAL VENDOR WIN_VENDOR
X-043223065 GP002134 GP002134
3065 X-043223065 USD005 P10011
3065 3065 X-043223065 EUR003 P10011
4567 4567 X-023065 UD00005 UD00005
4567 X-023065 DF00388 UD00005
4321 X-04065 P24005 P24005
4321 4321 X-04065 D41111 P24005
4321 X-04065 D46732 P24005
X-0432065 US7800 D0230005
X-0432065 EUR234 D067805
123 123 X-04322 P0008 P0008
123 1234 X-04322 EU0323 P0008
123 1262 X-04322 EUR0032 P0008
2345 2345 X-04322 DFGH322 P12008
123456 123456 X-04322 EUR00323 P12008
1113 1113 X-04322 EUR0032 P12008
Logic for 1,2 and 3 sets of data:
Pick up the MATERIAL AND WIN_VENDOR combination and get the unique MAT_CODE and apply it across all MATERIAL- WIN_VENDOR combinations as the NEW_MATCODE
Logic for 4th set :
If no combination for MAT_CODE exists then leave it as-is
Logic for 5th set:
When different MAT_CODE exists for the same MATERIAL and WIN_VENDOR combination, apply NEW_MATCODE as the MAT_CODE from MATERIAL - VENDOR where VENDOR = WIN_VENDOR
Logic for 6th set:
When different MAT_CODE exists for the same MATERIAL and WIN_VENDOR combination, and VENDOR <> WIN_VENDOR leave MAT_CODE as-is.
Hope it is clear. Any help would be appreciated.
Thanks.
I think the following query will get you most of the way to what you are looking for:
SELECT mat_code, material, vendor, win_vendor,
CASE
WHEN COUNT(DISTINCT mat_code) OVER (PARTITION BY material, win_vendor) = 0 THEN mat_code
WHEN COUNT(DISTINCT mat_code) OVER (PARTITION BY material, win_vendor) = 1 THEN MAX(mat_code) OVER (PARTITION BY material, win_vendor)
ELSE NVL((SELECT sub.mat_code FROM material_info sub WHERE sub.material = mi.material AND sub.vendor = sub.win_vendor), mi.mat_code)
END AS NEW_MAT
FROM material_info mi;
The case statement is making use of the analytical functions to handle cases 1-4. The else branch is attempting to grab Case 5 and if it isn't found defaulting to Case 6.

Get data using JOIN

I have two tables having following data-
Social_Tbl
ID Name Value
------------------------
1 Facebook FB
2 Orkut OR
3 Google GL
4 Other OT
And Organization_tbl
ID Organization Name
-----------------------------
1 1234 Facebook
2 1234 Google
3 146 Other
4 126 Other
5 126 Facebook
6 77 Google
Here, 'Name' is the foreign key (Not ID).
I want to join these tables and get the 'Name' columns data which does not belong to organization id 1234. As follows-
Name
----
Orkut
Other
Here, 'Orkut' and 'Other' does not belong to 1234 organization.
I tried following query for this-
select * from Social_Tbl st
join Organization_tbl ot
on st.Name = ot.Name
where Organization = 1234
This query fetches Names related to 1234 i.e Facebook and Google. I want result
Orkut and Other. If I replace Organization = 1234 with Organization != 1234 it returns all data from Organization_tbl.
Can somebody help me on this. This should be pretty simple, just npt able to find it out.
Could be done with a subquery:
select st.Name
from Social_Tbl st
where not exists (
select *
from Organization_tbl ot
where st.Name = ot.Name
and ot.Organization = 1234
)
(This also returns names that don't have an entry in Organization_tbl at all.)

Postgresql Query: How to return all parent records when certain criteria for all child records are met

I'm working with a SQL table, 'contacts' that includes the fields 'contact_id', 'account_id', and 'approved'.
Accounts are companies that are potential customers, and there are multiple contact people per account. If any of those contact people are approved (meets certain criteria), we can sell to the account. The 'approved' field is boolean.
I'm trying to write a query that will return account_ids for accounts where no contact is approved. I've tried playing around with ANY, BOOL_AND and several types of grouping and counting with no success. Any suggestions are much appreciated.
My table resembles this:
ACCOUNT_ID CONTACT_ID APPROVED
Apple 123 TRUE
Apple 321 FALSE
Pear 456 FALSE
Pear 654 FALSE
Orange 789 TRUE
Orange 987 TRUE
I would like my query to return
ACCOUNT_ID
Pear
Because this is the only account for which all records are false.
You could try to use NOT EXISTS
SELECT DISTINCT account_id
FROM contacts a
WHERE
NOT EXISTS (
SELECT 1
FROM contacts b
WHERE a.account_id = b.account_id AND b.approved = TRUE)
I've created a demo here

Pairing Send and Receive Data Rows in SQL

Having issues pairing data rows in SQL to identify when an item goes out, but does not return. The send and receive entries are on different rows, and there is a data relationship between the two, so I think this is doable, I'm just stumped as to the solution. The Module and User should tie together the transaction, however there is additionally a "batch" column that could be used to further ensure a unique transaction
Example Table Structure:
MODULE USER EVENTDTTM ACTION Batch
--------------------------------------------------------------------
MODULE1 USERB 2016-01-09 13:00:00 SENT 001
MODULE1 USERB 2016-01-09 13:01:00 RECEIVED 001
MODULE2 USERA 2016-01-09 13:00:00 SENT 001
MODULE2 USERA 2016-01-09 13:01:00 RECEIVED 001
MODULE1 USERA 2016-01-09 13:03:00 SENT 002
MODULE2 USERB 2016-01-09 13:04:00 SENT 002
I've tried a to do a join on the table itself, but I'm only seeing the paired data (with crazy amounts of duplicates)...I cant find the scenarios where an item was sent, but no paired receipt was found.
select *
from
(select * from T where Action = 'SENT') s
left outer join
(select * from T where Action = 'RECEIVED') r
on r.Module and s.Module and r.User = s.User and r.Batch = s.Batch
From the limited amount of sample data it appears that you can uniquely determine a match by having a common module, user and batch. I'm not sure why you came up with duplicates in your queries. The only other issue appears to be using an outer join to keep the "sends" that don't have a "receive" yet.
I think you still wanted everything in the result. If you did only want the unpaired scenarios then add:
where r.Module is null
SELECT
T1.module,
T1.user,
T1.eventdttm,
T1.action,
T1.batch
FROM
My_Table T1
WHERE
T1.action = 'SENT' AND
NOT EXISTS
(
SELECT *
FROM My_Table T2
WHERE
T2.module = T1.module AND
T2.user = T1.user AND
T2.batch = T1.batch AND
T2.action = 'RECEIVED'
)

Compare 2 values of different types inside of subquery

I am using a MS SQL db and I have 3 tables: 'base_info', 'messages', 'config'
bases:
ID Name NameNum
====================================
1 Home 101
2 Castle 102
3 Car 103
messages:
ID Signal RecBy HQ
============================
111 120 Home 1
111 110 Castle 1
111 125 Car 1
222 120 Home 2
222 125 Castle 2
222 130 Car 2
333 100 Home 1
333 110 Car 2
config:
ID SignalRec SignalOut RecBy HQ
====================================
111 60 45 101 1
111 40 60 102 1
222 50 60 102 2
222 30 90 101 2
333 80 10 103 1
Ok so now I have a subquery in which I select the 'SignalRec' and 'SignalOut' from the config table and match it on the messages table by ID and Date(not included above), the problem is that I need it to match where messages.RecBy = config.RecBy but config.RecBy is a string but it's equivalent Name is in the bases table. So I almost need to do a subquery inside a subquery or some type of join and compare the returned value.
Here is what I have so far:
(SELECT TOP 1 config.SignalRec from config WHERE config.ID = messages.ID AND ||I need th other comparison here||...Order By...) As cfgSignalRec,
(SELECT TOP 1 config.SignalOut from config WHERE config.ID = messages.ID AND ||I need th other comparison here||...Order By...) As cfgSignalOut
I tried to make this as clear as possible but if you need more info let me know.
I would normalize out RecBy in your messages table to reference the bases table. Why would you insert the string content there if it's also referenced in bases?
This is exactly why normalization exists: reduce redundancy, reduce ambiguity, and enforce referential integrity.
To make this more clear, RecBy in the messages table should be a foreign key to Bases.
I think this could do the trick (although I have not tried it...)
SELECT
c.SignalRec
FROM config c
INNER JOIN bases b
ON c.RecBy = b.NameNum
INNER JOIN messages m
ON b.Name = m.RecBy
WHERE c.ID = m.ID
However, as Anthony pointed out, you probably want to normalize out the strings in the RecBy column in the messages table, as you have the same data in the bases table.
From your description, it just sounds like you need two JOINS
SELECT TOP 1
c.SignalRec
FROM
config c
INNER JOIN
bases b
ON c.RecBy = b.NameNum
INNER JOIN
messages m
ON b.Name = m.RecBy
I think I might have not been clear enough what I wanted to do, sorry about that.
The data is actually different in the 2 tables, although the correlations are the same. It's kind of confusing to explain without going into detail about how the system works.
I actually found a very fast way of doing this.
Inside my sub-query I do this:
(SELECT TOP 1 config.Signal FROM config,bases
WHERE config.ID = messages.ID AND bases.Name = messages.RecBy AND bases.NameNum =
config.RecBy Order By...)
So this essentially compares the 2 RecBy's of different tables even though one is an integer and the other is a string. It reminds me of a match and look up in Excel.