Pairing Send and Receive Data Rows in SQL - 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'
)

Related

How to select rows where fields change

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.

Access Query mysteriously omitting records

I have an MS Access Contact & Correspondence database consisting of a "Contacts" table (Name, phone, email, etc.) and a "Calls" table (Contact, Incoming/Outgoing, Time, Medium, Notes, etc.). The tables are linked on Contacts.ID = Calls.Contact
For example:
NAME NUMBER EMAIL
Michaelangelo 123-4567 M#TMNT.com
Donatelo 123-4567 d#TMNT.com
Leonardo 123-4567 L#TMNT.com
Raphael 123-4567 R#TMNT.com
CONTACT TIME IN/OUT
Michaelangelo 1/1/2019 Outgoing
Michaelangelo 1/15/2019 Incoming
Michaelangelo 2/1/2019 Outgoing
Michaelangelo 3/1/2019 Outgoing
Leonardo 1/1/2019 Outgoing
Leonardo 2/1/2019 Outgoing
Michaelangelo 3/15/2019 Incoming
I'm trying to build a query to report all the contacts information (just as when the table is opened up directly), but with a field showing the most recent Incoming and Outgoing correspondence.
So, for the above data:
NAME LAST OUT LAST IN NUMBER EMAIL
Michaelangelo 3/1/2019 3/15/2019 123-4567 M#TMNT.com
Donatelo 123-4567 d#TMNT.com
Leonardo 2/1/2019 123-4567 L#TMNT.com
Raphael 123-4567 R#TMNT.com
So the results would independently identify the latest date of a correspondence record of both the incoming and outgoing types, and would return [nulls] wherever such a correspondence type did not yet exist.
I have a query that is working, mostly. However it seems to be mysteriously omitting certain records for which there are no records in 'Calls'. Not all such records, mind you.
My existing, troubled code is shown below. I have also tried moving the WHERE statements inside the JOINS (before ON); I have tried opening the WHERE statement with Calls.Time IS NULL OR Calls_1.Time IS NULL OR …
, and several other versions of the WHERE statement.
Here is the existing query:
SELECT Contacts.Name_First, Contacts.Name_Last, Max(Calls.Time) AS [Last Incoming], Max(Calls_1.Time) AS [Last Outgoing]
FROM (Contacts
LEFT OUTER JOIN Calls AS Calls_1 ON Contacts.ID = Calls_1.Contact )
LEFT OUTER JOIN Calls ON Contacts.ID = Calls.Contact
WHERE (
(((Calls.Outgoing_Incoming)="Incoming") OR Calls.Outgoing_Incoming IS NULL)
AND
(((Calls_1.Outgoing_Incoming)="Outgoing") OR Calls_1.Outgoing_Incoming IS NULL)
)
GROUP BY Contacts.Name_First, Contacts.Name_Last;
The full 'Contacts' table has 361 records. The intended result is that all 361 records will be returned, whether they have corresponding records in 'Calls' or not.
In fact only 208 records are returned. Many of these do not have corresponding 'Calls' records, telling me that the OR NULL statements are working, at least partly. I cannot find any consistent distinction between the records that are omitted verses the records that are returned.
Do the aggregation before doing the JOIN. Then you only need to aggregate once:
SELECT c.Name_First, c.Name_Last, ca.Last_Incoming, ca.Last_Outgoing
FROM Contacts as c LEFT OUTER JOIN
(SELECT Contact,
MAX(IIF(Outgoing_Incoming IS NULL OR Outgoing_Incoming = "Incoming", Time, NULL)) as Last_Incoming,
MAX(IIF(Outgoing_Incoming IS NULL OR Outgoing_Incoming = "Outgoing", Time, NULL)) as Last_Outgoing,
FROM Calls
GROUP BY Contact
) as ca
ON c.ID = c.Contact ;

How can I generate a list while ignoring records that have a date that does not fit into the range I am looking for?

I am using Microsoft SQL Server, I currently have a table with records for accounts. These master accounts can have several sub accounts linked to them. For example, master account XXX can have sub account XXXA and XXXB and... XXXN and so on and so forth.
These sub accounts can be opened and added to the master account XXX across time, so at different points in time. When a new sub account is opened, it also opens a new master account. From that point on, other sub accounts can be added to that master account number.
I have a column with the account opening dates. These dates are linked to when the sub accounts are opened.
I am trying to generate a list of master accounts (not sub accounts), that were opened between 2018-11-01 and 2019-02-15. However, I only want to include new MASTER ACCOUNTS, therefore ignoring any master accounts that have an account opening date prior to 2018-11-01.
The issue I am having is master accounts that are showing up in my generated list because they have sub accounts that have been added to them during the date ranges I am looking for.
I Have tried using the MIN function inside of having on my dates. I have checked other stack overflow threads for a solution as well
SELECT master_accounts, accountopendate, accountclosedate
FROM accounts
GROUP BY master_accounts, accountopendate, accountclosedate
HAVING MIN(accountopendate) BETWEEN '2018-11-01' AND '2019-02-15';
It gave me a list of the master accounts, however upon doing some QA, I find some master accounts in the list, that have been opened prior to 2018-11-01.
I would like a list of master accounts with the oldest account opening date being 2018-11-01, ignoring all the master accounts with account opening dates prior to 2018-11-01.
EXPECTED RESULT:
+-----------------+-----------------+------------------+
| master_accounts | accountopendate | accountclosedate |
+-----------------+-----------------+------------------+
| XXX | 2018-11-01 | NULL |
| ZZZ | 2018-12-01 | NULL |
| YYY | 2019-02-01 | NULL |
+-----------------+-----------------+------------------+
This should work, assuming the earliest opening date is always going to include the master account number.
First, isolate the account numbers and the initial opening date, then join that result set to your base table. I used a CTE, but a sub-query would accomplish the same thing.
Using a CTE:
WITH masterOpen AS
(
SELECT
master_accounts
,MIN(accountopendate) AS openDate
FROM
dbo.accounts
GROUP BY
master_accounts
)
SELECT
a.master_accounts
,a.accountopendate
,a.accountclosedate
FROM
dbo.accounts AS a
JOIN
masterOpen AS mo
ON
mo.master_accounts = a.master_accounts
AND
mo.openDate = a.accountopendate
AND
mo.openDate >= '2018-11-01'
AND
mo.openDate <= '2019-02-15';
Sub-query instead:
SELECT
a.master_accounts
,a.accountopendate
,a.accountclosedate
FROM
(
SELECT
master_accounts
,MIN(accountopendate) AS openDate
FROM
dbo.accounts
GROUP BY
master_accounts
) AS mo
JOIN
dbo.accounts AS a
ON
mo.master_accounts = a.master_accounts
AND
mo.openDate = a.accountopendate
AND
mo.openDate >= '2018-11-01'
AND
mo.openDate <= '2019-02-15';
The date parameters could also be broken out into a WHERE clause if you prefer, but with an INNER JOIN it will yield the same results. For current versions of the SQL engine, it's more a matter of preference than performance.
Why not just use filter
SELECT distinct master_accounts, accountopendate, accountclosedate
FROM accounts where accountopendate>='2018-11-01' AND accountopendate<='2019-02-15'

Compare a date with a block of dates in SQL

i tried to ready a lot of date comparisons that i found here on stackoverflow and spread into the internet but i wasn't able to find the solution.
I have the following table (Trips):
VehicleID DriverID xID CheckIn CheckOut DateHour
462 257 7 1 0 16/12/2017 20:40:00
462 257 7 0 1 19/12/2017 10:05:00
5032 3746 11 1 0 02/10/2017 07:00:00
5032 3746 11 0 1 06/10/2017 17:00:00
When my company receives a traffic ticket, i want to compare the date from the ticket with the hole block of dates from the table "Trips", each block starts with CheckIn = 1 and finishes with CheckOut = 1, so this way i will know which driver was responsable for the ticket through the DriverID.
For example: the traffic ticket date and time are: 17/12/2017 08:00:00 and the Vehicle is the one with id = 462, i'll insert this date and time in a field in our system to consult automaticaly which driver was driving that car at that moment, we won´t use the ticket table yet. Looking at my example, i know it should return DriverID = 257, but theres a lot of trips with the same vehicle and diferent drivers.....The major problem is how can i compare the Date and Hour from the Ticket with the range of dates from the trips, since i have to consider 1 trip = 2 lines in the table
Unfortunately i can't change the way this table was created, cause we need this 2 lines, CheckIn and CheckOut, separately.
Any thoughts or directions?
Thank you for your attention
select t1.VehicleID
,t1.DriverID
,t1.xID
,t1.DateHour as Checkin
,t2.DateHour as Checkout
from trips as t1 join trips as t2 --self join trips to get both start and end in a single row
on t1.VehicleID = t2.VehicleID -- add all columns
and t1.DriverID = t2.DriverID -- which define
and t1.xID = t2.xID -- a unique trip
and t1.Checkin = 1 -- start
and t2.Checkout = 1 -- end
join tickets -- now join tickets
on tickets.trafficDateHour between t1.DateHour and t2.DateHour
I didn't make sample tables, this will not run as is, but something like this should do it for you:
SELECT *
FROM tickets, trips
WHERE
trips.datehour in (
SELECT trips.datehour
FROM tickets, trips
WHERE
tickets.ticket_date < trips.datehour AND
trips.checkin = 0
) AND
tickets.ticket_date > trips.datehour AND
trips.checkin = 1
If you are running this for a specific date as described in the comment above, it will work. If you are trying to run it for a set of ticket dates all at once, you'll require recursion. Recursion is a different beast depending on your flavor of SQL.

sql query condition in oracle report IF ELSE

I have The following tables:
DOCUMENT(iddoc,doctype,title,publishingdate,validTillDate)
USERS(iduser,fname,lname)
TRANSACTION(idtrans,iduser,iddoc,transdate,schedulereturndate)
I'm asked to Indicate for a given document whether it is available or not and if it is borrowed by whom, and when it should be returned. So how can i have these conditions in my query.
where my code will be like this:
if(d.validTillDate < SYSDATE){
SELECT u.iduser t.schedulereturndate
FROM USERS u, TRANSACTION t
WHERE u.iduser=t.iduser
}
SO I WANNA KNOW HOW CAN I CODE THIS IF
The query for a borrowed document would be something like this:
SELECT d.iddoc,u.iduser t.schedulereturndate,'Borrowed'
from document d,
,USERS u
,TRANSACTION t
WHERE u.iduser=t.iduser
and t.iddoc=d.iddoc
and d.validitydate<sysdate
union
SELECT d.iddoc,null,null,'Not borrowed'
from document d,
WHERE d.validitydate is null
or d.validitydate>=sysdate
Edit ) added a union for the not borrowed documents.
It's hard to understand your question for me.
If I guessed right, then:
SELECT d.iddoc, u.iduser, t.schedulereturndate
FROM
document d
LEFT JOIN transaction t ON
(d.iddoc=t.iddoc)
-- join by iddoc field
AND (SYSDATE BETWEEN t.transdate AND t.schedulereturndate)
-- I assume we need only current transactions, not past (or future?)
WHERE
(SYSDATE<=d.validTillDate)
-- I assume we need only documents whose validity date not passed yet
Assuming there are no active transactions for iddoc=1, one active transaction for iddoc=2 and two active transactions for iddoc=3, the result will look like:
iddoc | iduser | schedulereturndate
------+--------+-------------------
1 NULL NULL
2 534 2017-09-08
3 54334 2016-03-02
3 2433 2016-07-01