Join two tables based on date from first - sql

I have two tables like below (date format: yyyy-MM-dd):
1) Table1 - SURGERY
P_ID | SURGERY_DATE
------------------------------------------------
1 | 2012-04-01
2 | 2012-08-14
1 | 2012-07-22
4 | 2012-10-30
3 | 2012-06-07
2) Table2 - VISIT
P_ID | VISIT_DATE
-----------------------------------------
1 | 2012-03-28
1 | 2012-04-14
1 | 2012-05-17
1 | 2012-09-12
3 | 2012-07-15
4 | 2012-10-10
3 | 2012-06-01
The tables SURGERY and VISIT are joined from other tables. I would like to find all records that meet the following criteria: VISIT_DATE >= SURGERY_DATE
3) Result table
EMPLOYEE_ID | SUGERY_DATE | NUMBER OF VISIT
-------------------------------------------------------
1 | 2012-04-01 | 4
2 | 2012-08-14 | 0
1 | 2012-07-22 | 2
4 | 2012-10-30 | 1
3 | 2012-06-07 | 1

Using group by and count can solve your problem.
Please try the code below.
(https://i.stack.imgur.com/NFzdf.jpg)

You can use a correlated subquery:
select s.*,
(select count(*)
from visit v
where v.p_id = s.p_id and v.visit_date > s.surgery_date
) as num_visits_after
from surgery s;

You need to use group by and count with a mentioned condition like the following:
SELECT
S.P_ID,
S.SURGERY_DATE,
SUM(CASE
WHEN V.VISIT_DATE > S.SURGERY_DATE THEN 1
END) AS NUM_VISITS_AFTER
FROM
SURGERY S
LEFT JOIN VISIT V ON ( S.P_ID = V.P_ID )
GROUP BY
S.P_ID,
S.SURGERY_DATE;
Cheers!!

Related

postgresql query to generate report with multiple columns

I'm having a customer transaction table in postgresql db with the below columns
transactionId (primary)| customerId(int8)| transactionDate (timestamp)
1 2 2020-02-14
2 3 2020-01-08
3 1 2020-02-06
4 2 2020-02-13
5 2 2020-03-24
Need to build a query to create the report of the below
CustomerId| FirstTransaction| TotalTransactions| Transactions/Week| RecentTransactions
1 2020-02-06 1 1 2020-02-06
3 2020-01-08 1 1 2020-01-08
2 2020-02-13 3 2 2020-03-24
When the customer first started at first, total transactions, Frequency per week, Recency of last?
and the report should consider(contain) last 3 months records only.
Try the following, here is the demo.
with cte as
(
select
*,
count(*) over (partition by customerId) as totalTransactions,
1 + floor((extract(day from transactionDate) - 1) / 7) as transactionsWeek
from myTable
where transactionDate >= '2020-01-01'
and transactionDate <= '2020-03-31'
)
select
customerId,
min(transactionDate) as firstTransaction,
max(totalTransactions) as totalTransactions,
max(transactionDate) as recentTransactions,
(ceil(avg(totalTransactions)/count(distinct transactionsWeek))::int) as "Transactions/Week"
from cte
group by
customerId
order by
customerId
Output:
| customerid | firsttransaction | totaltransactions | recenttransactions | Transactions/Week |
| ---------- | ------------------------ | ----------------- | ------------------------ | ----------------- |
| 1 | 2020-02-06 | 1 | 2020-02-06 | 1 |
| 2 | 2020-02-13 | 3 | 2020-03-24 | 2 |
| 3 | 2020-01-08 | 1 | 2020-01-08 | 1 |
for the last three months you can also use following in where condition
transactionDate > CURRENT_DATE - INTERVAL '3 months'

SQL: Find the longest date gap from multiple table

i need some help.
I have two tables like this.
Table Person
p_id | name | registration date
-----------------------------
1 | ABC | 2018-01-01
2 | DEF | 2018-02-02
3 | GHI | 2018-03-01
4 | JKL | 2018-01-02
5 | MNO | 2018-02-01
6 | PQR | 2018-03-02
Table Order
Order_id| p_id | order_date
----------------------------
123 | 1 | 2018-01-05
345 | 2 | 2018-02-06
678 | 3 | 2018-03-07
910 | 4 | 2018-01-08
012 | 3 | 2018-03-04
234 | 4 | 2018-01-05
567 | 5 | 2018-02-08
890 | 6 | 2018-03-09
I need to find out how many days is the longest period when this two table aren't updated.
What's the easiest query to get the result in SQL?
Thank you
UPDATE:
The result should be showing the longest date gap between order_date and registration_date. Because the longest date gap is 2018-01-08 and 2018-02-01, so the result should return '24'
Try this:
SELECT MAX(DATE_PART('day', now() - '2018-02-15'::TIMESTAMP)) FROM person p
JOIN order o
USING (p_id)
Assuming current PostgreSQL and lots of orders per person on avg., this should be among the fastest options:
SELECT o.order_date - p.registration_date AS days
FROM person p
CROSS JOIN LATERAL (
SELECT order_date
FROM "order" -- order is a reserved word!
WHRE p_id = p.p_id
ORDER BY 1 DESC -- assuming NOT NULL
LIMIT 1
) o
ORDER BY 1 DESC
LIMIT 1;
Needs an index on "orders"(p_id, order_date).
Detailed explanation:
Optimize GROUP BY query to retrieve latest record per user
Select first row in each GROUP BY group?
You seem to want:
select max(o.order_date - p.registration_date)
from person p join
orders o
on p.p_id = o.p_id;
select max((date_part('day',age(order_date, "registration date")))) + 1 as dif
from (
select "p_id" ,max(order_date) order_date
from "Order"
group by "p_id"
) T1
left join Person T2 on T1.p_id = T2.p_id
| maxday |
|--------|
| 8 |
[SQL Fiddle DEMO LINK]

Get post status for each day from status change history

There is a table post_status_changes, which is history of post status changes
post_id | created_at | status
---------+---------------------+---------
3 | 2016-09-02 04:00:00 | 1
3 | 2016-09-04 19:59:21 | 2
6 | 2016-09-03 15:00:00 | 5
6 | 2016-09-03 19:52:46 | 1
6 | 2016-09-04 20:53:22 | 2
What I wanna get is a list for each day from DayA till DayB of post status for end of date.
DayA = 2016-09-01
DayB = 2016-09-05
post_id | date | status
-----------+-------------+---------
3 | 2016-09-01 | null
3 | 2016-09-02 | 1
3 | 2016-09-03 | 1
3 | 2016-09-04 | 2
3 | 2016-09-05 | 2
6 | 2016-09-01 | null
6 | 2016-09-02 | null
6 | 2016-09-03 | 1
6 | 2016-09-04 | 2
6 | 2016-09-05 | 2
Any solutions?
solution was found here: PHP: Return all dates between two dates in an array
$period = new DatePeriod(
new DateTime('2010-10-01'),
new DateInterval('P1D'),
new DateTime('2010-10-05')
);
foreach ($period as $each){
//.. QUERY here, where "CREAtED_AT" = $each
}
with a as
(select convert(varchar(10), created_at, 102) [date], [status],
post_id, rank() over (partition by convert(varchar(10), created_at),
post_id order by created_at desc) as r
from post_status_changes)
select post_id, [date], [status] from a where r =
(select top 1 r from a as a2 where a.[date] =
a2.[date] and a.[post_id] = a2.[post_id])
and #DayA <= [date] and #DayB >= [date] order by post_id, [date];
For each post_id you want as many rows as there are days between the start and end date. This can be done by cross joining the list of dates with the post_ids and then join that result back to the table to get the status for each day:
select x.post_id, t.created, p.status
from generate_series(date '2016-09-01', date '2016-09-05', interval '1' day) as t(created)
cross join (
select distinct post_id
from post_status_changes
) x
left join post_status_changes p on p.created_at::date = t.created
order by 1,2;
Running example: http://rextester.com/CSX38222

How can I do SQL query count based on certain criteria including row order

I've come across certain logic that I need for my SQL query. Given that I have a table as such:
+----------+-------+------------+
| product | valid | Date |
+----------+-------+------------+
| 1 | null | 2016-05-10 |
| 1 | null | 2016-05-09 |
| 1 | yes | 2016-05-08 |
+----------+-------+------------+
This table is produced by a simple query:
SELECT * FROM products WHERE product = 1 ORDER BY date desc
Now what I need to do is create a query to count the number of nulls for certain products by order of date until there is a yes value. So the above example the count would be 2 as there are 2 nulls until a yes.
+----------+-------+------------+
| product | valid | Date |
+----------+-------+------------+
| 2 | null | 2016-05-10 |
| 2 | yes | 2016-05-09 |
| 2 | null | 2016-05-08 |
+----------+-------+------------+
Above would return 1 as there is 1 null until a yes.
+----------+-------+------------+
| product | valid | Date |
+----------+-------+------------+
| 3 | yes | 2016-05-10 |
| 3 | yes | 2016-05-09 |
| 3 | null | 2016-05-08 |
+----------+-------+------------+
Above would return 0.
You need a Correlated Subquery like this:
SELECT COUNT(*)
FROM products AS p1
WHERE product = 1
AND Date >
( -- maximum date with 'yes'
SELECT MAX(Date)
FROM products AS p2
WHERE p1.product = p2.product
AND Valid = 'yes'
)
This should do it:
select count(1) from table where valid is null and date > (select min(date) from table where valid = 'yes')
Not sure if your logic provided covers all the possible weird and wonderful extreme scenarios but the following piece of code would do what you are after:
select a.product,
count(IIF(a.valid is null and a.date >maxdate,a.date,null)) as total
from sometable a
inner join (
select product, max(date) as Maxdate
from sometable where valid='yes' group by product
) b
on a.product=b.product group by a.product

SQL - summing up minutes in the table for all the rows with the same month as their date and store it in a column for each row

I have a table as follow:
id |minutes |sumOfMinutes|Date
_______________________________________
1 | 5 | | 20141106
1 | 7 | | 20141106
2 | 1 | | 20141106
2 | 9 | | 20141106
3 | 8 | | 20141106
How can I store sum of minutes in the third column for rows under the same month, so that i have:
id |minutes |sumOfMinutes| Date
_____________________________________
1 | 5 | 12 | 20141106
1 | 7 | 12 | 20141112
2 | 1 | 18 | 20141006
2 | 9 | 18 | 20141007
3 | 8 | 18 | 20141009
Use SUM() and Group by
SELECT table1.id, table1.minutes, SUM(monthTot.minutes), table1.Date
FROM table 1
JOIN table1 AS monthTot ON
MONTH(monthTot.date) = MONTH(table1.date)
GROUP BY table1.id, table1.minutes, table1.Date
sum with partition by option can be used to achieve this.
select id, [minutes],
sum([minutes]) over ( partition by month([date]) ) as sumOfMinutes,
[Date]
from Table1