SQL query min and max group by flag - sql

I have a table as below :
How can I craft a SQL select statement so that MIN AND MAX EVENT DATE groups results by FLAG (0,1)?
So the result would be:

Just do conditional aggregation with use of window function
SELECT card_no, descr_reader,
max(CASE WHEN flag = 0 THEN event_date END) date_in,
max(CASE WHEN flag = 1 THEN event_date END) date_out
FROM
(
SELECT *,
COUNT(flag) OVER (PARTITION BY flag ORDER BY id) Seq
FROM table t
)t
GROUP BY card_no, descr_reader, Seq

An alternative if Window function does not work:
SELECT
t1.card_no, t1.descr_reader,
t1.event_date date_in,
(select top 1 event_date from test t2
where t2.card_no = t1.card_no and
t2.reader_no = t1.reader_no and
t2.descr_reader = t1.descr_reader and
t2.event_date > t1.event_date and
t2.flag = 1
order by t2.event_date ) as date_out
FROM test t1
WHERE t1.flag = 0

Related

Oracle Join Between with Interval Times of Minutes

I have some problems with joining tables with different date interval by minutes.
Example :
table1
ID Modules Timestamp
1 Delivered 02-FEB-2020 08:24:45
1 Read 02-FEB-2020 08:27:50
1 Delivered 03-FEB-2020 09:24:45
1 Read 03-FEB-2020 10:00:50
2 Delivered 03-FEB-2020 09:28:10
2 Read 03-FEB-2020 09:30:11
Question:
is there any way to make the data become like this?
ID Modules1 Timestamp1 Modules2 Timestamp2
1 Delivered 02-FEB-2020 08:24:45 Read 02-FEB-2020 08:27:50
1 Delivered 03-FEB-2020 09:24:45
1 Read 03-FEB-2020 10:00:50
2 Delivered 03-FEB-2020 09:28:10 Read 03-FEB-2020 09:30:11
Goal:
so if someone read during 5 minutes then it will join, if not the data will remain same.
I interpret this as a type of gaps-and-islands problem. Each "island" either starts with a lag of 5 minutes on a "Read" or any row with "Delivered".
with tgrp as (
select t.*,
sum(case when modules = 'Delivered' or
prev_timestamp < timestamp - interval '5' minute
then 1 else 0
end) over (partition by id order by timestamp) as grp
from (select t.*,
lag(timestamp) over (partition by id order by timestamp) as prev_timestamp
from t
) t
)
select id,
max(case when seqnum = 1 then module end) as module1,
max(case when seqnum = 1 then timestamp end) as timestamp1,
max(case when seqnum = 2 then module end) as module2,
max(case when seqnum = 2 then timestamp end) as timestamp2
from (select tgrp.*,
row_number() over (partition by id, grp order by timestamp) as seqnum
from tgrp
) tgrp
group by id, grp;
EDIT:
I think a simpler method is to put the data together using lead() and then filter and adjust the final values:
select t.id, t.module, t.timestamp,
(case when t.next_module = 'Read' and
t.next_timestamp < t.timestamp + interval '5' minute
then t.next_module
end) as module2,
(case when t.next_module = 'Read' and
t.next_timestamp < t.timestamp + interval '5' minute
then t.next_timestamp
end) as timestamp2
from (select t.*,
lead(module) over (partition by id order by timestamp) as next_module,
lead(timestamp) over (partition by id order by timestamp) as next_timestamp
from t
) t
where module = 'Delivery' or
(next_timestamp > timestamp + interval '5' minute)
You can do self join to achieve the desired result as following:
With cte as
(Select t.*,
Row_number() over (partition by id, modules order by timestamp) as rn
From your_table t)
Select t1.*,
case when t1.modules = 'delivered' and t1.timestamp + interval '5' minute <= t2.timestamp
then t2.timestamp
end as timestamp2
From cte t1
left join cte t2
On (t1.rn = t2.rn and t2.modules = 'read')
Left join cte3
On (t1.rn = t3.rn and t3.modules = 'delivered')
Where t1.modules = 'delivered' or t3.timestamp + interval '5' minute > t2.timestamp
Cheers!!

MSSQL Group by and Select rows from grouping

I'm trying to figure out if what I'm trying to do is possible. Instead of resorting to multiple queries on a table, I wanted to group the records by business date and id then group by the id and select one date for a field and another date for the other field.
SELECT
*
{AMOUNT FROM DATE}
{AMOUNT FROM OTHER DATE}
FROM (
SELECT
date,
id,
SUM(amount) AS amount
FROM
table
GROUP BY id, date
AS subquery
GROUP BY id
It seems that you're looking to do a pivot query. I usually use cross tabs for this. Based on the query you posted, it could look like:
SELECT
id,
SUM(CASE WHEN date = '20190901' THEN amount ELSE 0 END) AmountFromSept01,
SUM(CASE WHEN date = '20191001' THEN amount ELSE 0 END) AmountFromOct01
FROM (
SELECT
date,
id,
SUM(amount) AS amount
FROM
table
GROUP BY id, date
)AS subquery
GROUP BY id;
You could also use a CTE.
WITH CTE AS(
SELECT
date,
id,
SUM(amount) AS amount
FROM
table
GROUP BY id, date
)
SELECT
id,
SUM(CASE WHEN date = '20190901' THEN amount ELSE 0 END) AmountFromSept01,
SUM(CASE WHEN date = '20191001' THEN amount ELSE 0 END) AmountFromOct01
FROM CTE
GROUP BY id;
Or even be a rebel and do the operation directly.
SELECT
id,
SUM(CASE WHEN date = '20190901' THEN amount ELSE 0 END) AmountFromSept01,
SUM(CASE WHEN date = '20191001' THEN amount ELSE 0 END) AmountFromOct01
FROM CTE
GROUP BY id;
However, some people have tested for performance and found that pre-aggregating can improve performance.
If I understand you correctly, then you're just trying to pivot, but only with two particular dates:
select id,
date1 = sum(iif(date = '2000-01-01', amount, null)),
date2 = sum(iif(date = '2000-01-02', amount, null))
from [table]
group by id

Window Functions

I want to add a window functions.
Take the min date when visit = Y and end as Associd.
TableA
ID Date AssocId Visit
1 1/1/17 10101 Y
1 1/2/17 10102 Y
End Results.
ID Date AssocId
1 1/1/17 10101
SQL > This gives me the min date but I need the AssocId associated to that date.
SELECT MIN(CASE WHEN A.VISIT = 'Y'
THEN A.DATE END) OVER (PARTITION BY ID)
AS MIN_DT,
You can use FIRST_VALUE():
SELECT MIN(CASE WHEN A.VISIT = 'Y' THEN A.DATE END) OVER (PARTITION BY ID) AS MIN_DT,
FIRST_VALUE(CASE WHEN A.VISIT = 'Y' THEN A.ASSOCID END) KEEP (DENSE_RANK FIRST OVER (PARTITION BY ID ORDER BY A.VISIT DESC, A.DATE ASC),
Note that this is a little tricky with conditional operations. I would be more inclined to use a subquery to nest the query operations. The outer expression would be:
SELECT MAX(CASE WHEN Date = MIN_DT THEN ASSOCID END) OVER (PARTITION BY ID)
If you wanted this per ID, I would suggest:
select id, min(date),
first_value(associd) over (partition by id order by date)
from t
where visit = 'Y'
group by id;
That is, use aggregation functions.
You seems want :
select t.*
from table t
where visit = 'Y' and
date= (select min(t1.date) from table t1 where t1.id = t.id);

Aggregation disables window function capability

I am trying to re-write the query where I am joining the query on itself:
select count(distinct case when cancelled_client_id is null and year(RUM.first_date) = year(date) and RUM.first_date <= .date then user_id
when cancelled_client_id is null and year(coalesce(RUM.first_date,RUR.first_date)) = year(date)
and coalesce(RUM.first_date,RUR.first_date) <= RUL.date then user_id end) as
from RUL
left join
(
select enrolled_client_id, min(date) as first_date
from RUL
where enrolled_client_id is not null
group by enrolled_client_id
) RUR on RUR.enrolled_client_id=RUL.enrolled_client_id
left join
(
select managed_client_id, min(date) as first_date
from RUL
where managed_client_id is not null
group by managed_client_id
) RUM on RUM.managed_client_id=RUL.managed_client_id
Using window functions:
count(distinct case when cancelled_client_id is null
and year(min(case when enrolled_client_id is not null then date end) over(partition by enrolled_client_id)) = year(date)
and min(case when enrolled_client_id is not null then date end) over(partition by enrolled_client_id) <= date
then user_id
when cancelled_client_id_rev is null
and year(coalesce(
min(case when enrolled_client_id is not null then date end) over(partition by enrolled_client_id),
min(case when managed_client_id is not null then date end) over(partition by managed_client_id))) = year(date)
and coalesce(
min(case when enrolled_client_id is not null then date end) over(partition by enrolled_client_id),
min(case when managed_client_id is not null then date end) over(partition by managed_client_id)) <= date
then user_id end)
from RUL
However I am getting an error that "Windowed functions cannot be used in the context of another windowed function or aggregate" due to the count(distinct min). Any work-arounds?
I have no idea what the count(distinct) is supposed to be doing, but you can simplify the code to:
select count(distinct case when cancelled_client_id is null and
year(rum_first_date) = year(date) and
rum_first_date <= rul.date
then user_id
when cancelled_client_id is null and
year(coalesce(RUM_first_date, RUR_first_date)) = year(rul.date) and
coalesce(rum_first_date, rur_first_date) <= RUL.date
then user_id
end) as . . .
from (select RUL.*,
min(date) over (partition by enrolled_client_id) as rur_date,
min(date) over (partition by managed_client_id) as rum_date
from RUL
) RUL

SemanticException Failed to breakup Windowing invocations into Groups. At least 1 group must only depend on input columns

The below query is working fine in Oracle but it is not working in hive.
SELECT Q.tm_mo_id,
'1380' AS mrc_cd,
NVL (R.itm_profit_ctr_cd, '99') AS profit_center_cd,
MAX(CASE R.itm_profit_ctr_cd
WHEN NULL THEN 'UNASSIGN PROFIT CNTR'
ELSE R.itm_profit_ctr_ds
END) profit_center_desc,
SUM(Q.bp_grs_quota_am) AS mth_bp_plan_gts_am_usd,
SUM(Q.grs_quota_am) AS mth_ju_plan_gts_am_usd
FROM v_l_0002_gb_gds_us_quota_v_1 Q
LEFT JOIN
(SELECT * FROM
(SELECT ph_dtl_id,
itm_profit_ctr_cd,
MIN (itm_profit_ctr_ds) AS itm_profit_ctr_ds,
ROW_NUMBER () OVER (
PARTITION BY ph_dtl_id
ORDER BY COUNT(CASE profit_ctr_cd
WHEN 'JNJDUMMY' THEN NULL
WHEN '99' THEN NULL
ELSE profit_ctr_cd
END) DESC,
itm_profit_ctr_cd ASC) rn
FROM v_l_0002_gb_gds_us_sku_to_profit_center_lookup_v_1
GROUP BY ph_dtl_id,
itm_profit_ctr_cd) E
WHERE rn = 1 ) R
ON (Q.ph_dtl_id = R.ph_dtl_id)
WHERE SUBSTR (Q.tm_mo_id, 1, 4) = '2016'
GROUP BY Q.tm_mo_id,
NVL(R.itm_profit_ctr_cd, '99')