I'm trying to select from table (sorted):
+--------+-------+
| Serial | Group |
+--------+-------+
| 0100 | 99 |
| 0101 | 99 |
| 0102 | 99 |
| 096 | 92 |
| 097 | 92 |
| 099 | 93 |
| 23 | 16 |
| 95 | 87 |
| 99 | 90 |
| 100 | 90 |
| 101 | 90 |
| 102 | 90 |
| a | a |
| b | b |
| c | c |
+--------+-------+
and I would like table (first, last and quantity by group):fsdfsdfsdfdsfsdf
+------------+----------+----------+
| fromSerial | toSerial | quantity |
+------------+----------+----------+
| 0100 | 0102 | 3 |
| 096 | 097 | 2 |
| 099 | 099 | 1 |
| 99 | 102 | 4 |
| 23 | 23 | 1 |
| 95 | 95 | 1 |
| a | a | 1 |
| b | b | 1 |
| c | c | 1 |
+------------+----------+----------+
My query
Thanks.
you can use window analytic function row_number to partition the data based on the group column
you can also get number of elements in each partition
you can then do case based aggregation to get the from and to serial number values.
SQL Fiddle Demo
with cte
as
(
select "Serial", "Group", row_number() over ( partition by "Group" order by "Serial" ) as rn,
count(*) over ( partition by "Group") as cnt
from Table1
)
select max(case when rn =1 then "Serial" end) as "FromSerial",
max(case when rn =cnt then "Serial" end) as "ToSerial",
max(cnt) as quantity
from cte
group by "Group"
Use MIN, MAX and GROUP BY.
And, DO NOT use keyword GROUP as column name.
SQL> WITH DATA AS(
2 SELECT '0100' Serial, '99' "GROUP_1" FROM dual UNION ALL
3 SELECT '0101' , '99' FROM dual UNION ALL
4 SELECT '0102' , '99' FROM dual UNION ALL
5 SELECT '096' , '92' FROM dual UNION ALL
6 SELECT '097' , '92' FROM dual UNION ALL
7 SELECT '099', '93' FROM dual UNION ALL
8 SELECT '23' , '16' FROM dual UNION ALL
9 SELECT '95' , '87' FROM dual UNION ALL
10 SELECT '99' , '90' FROM dual UNION ALL
11 SELECT '100' , '90' FROM dual UNION ALL
12 SELECT '101' , '90' FROM dual UNION ALL
13 SELECT '102' , '90' FROM dual UNION ALL
14 SELECT 'A' , 'A' FROM dual UNION ALL
15 SELECT 'b' , 'b' FROM dual UNION ALL
16 SELECT 'c' , 'c' FROM dual
17 )
18 SELECT MIN(serial) fromserial,
19 MAX(Serial) toserial,
20 COUNT(*) quantity
21 FROM DATA
22 GROUP BY group_1
23 ORDER BY fromserial
24 /
FROM TOSE QUANTITY
---- ---- ----------
0100 0102 3
096 097 2
099 099 1
100 99 4
23 23 1
95 95 1
A A 1
b b 1
c c 1
9 rows selected.
SQL>
Try this query :
SELECT grp,
Cast(Min(Cast(serial AS INT)) AS VARCHAR2(30)) fromserial,
Cast(Max(Cast(serial AS INT)) AS VARCHAR2(30)) toserial,
Count(*) quantity
FROM yourtable
WHERE NVL(LENGTH(TRIM(TRANSLATE(serial, '0123456789', ' '))), 0) = 0
GROUP BY grp
UNION
SELECT grp,
Cast(Min(serial) AS VARCHAR2(30)) fromserial,
Cast(Max(serial) AS VARCHAR2(30)) toserial,
Count(*) quantity
FROM yourtable
WHERE NVL(LENGTH(TRIM(TRANSLATE(serial, '0123456789', ' '))), 0) != 0
GROUP BY grp
ORDER BY grp
Sqlfiddle
Related
Suppose I have the following table t_1 where every row represents a day:
+------+------------+-------+
| week | date | val |
+------+------------+-------+
| 1 | 2022-02-07 | 1 | <- Monday
| 1 | 2022-02-08 | 2 |
| 1 | 2022-02-09 | 3 |
| 1 | 2022-02-10 | 4 | <- Thursday
| 1 | 2022-02-11 | 5 |
| 1 | 2022-02-12 | 6 |
| 1 | 2022-02-13 | 7 |
| 2 | 2022-02-14 | 8 | <- Monday
| 2 | 2022-02-15 | 9 |
| 2 | 2022-02-16 | 10 |
| 2 | 2022-02-17 | 11 | <- Thursday
| 2 | 2022-02-18 | 12 |
| 2 | 2022-02-19 | 13 |
| 2 | 2022-02-20 | 14 |
+------+------------+-------+
How can I create the following table t2 from t1?
+------------+------------+-----------+------------+
| date_start | date_end | val_cur. | val_prev |
+------------+------------+-----------+------------+
| 2022-01-14 | 2022-01-17 | 38 | 10 |
+------------+------------+-----------+------------+
Here val_cur is defined as the sum of values of the current timeframe (i.e. the sum of values between date_start and date_end) and val_prev is defined as the sum of values of the previous timeframe (i.e. the current timeframe minus one week).
-- Bigquery Standard SQL
WITH t_1 AS
(SELECT 1 AS week, '2022-02-07' AS date, 1 AS val UNION ALL
SELECT 1, '2022-02-08', 2 UNION ALL
SELECT 1, '2022-02-09', 3 UNION ALL
SELECT 1, '2022-02-10', 4 UNION ALL
SELECT 1, '2022-02-11', 5 UNION ALL
SELECT 1, '2022-02-12', 6 UNION ALL
SELECT 1, '2022-02-13', 7 UNION ALL
SELECT 2, '2022-02-14', 8 UNION ALL
SELECT 2, '2022-02-15', 9 UNION ALL
SELECT 2, '2022-02-16', 10 UNION ALL
SELECT 2, '2022-02-17', 11 UNION ALL
SELECT 2, '2022-02-18', 12 UNION ALL
SELECT 2, '2022-02-19', 13 UNION ALL
SELECT 2, '2022-02-20', 14)
SELECT '2022-02-14' AS date_start, '2022-02-17' AS date_stop, sum(val) AS val_cur
FROM t_1
WHERE date >= '2022-02-14' AND date <= '2022-02-17'
Output:
+-----+------------+------------+---------+
| Row | date_start | date_stop | val_cur |
+-----+------------+------------+---------+
| 1 | 2022-02-14 | 2022-02-17 | 38 |
+-----+------------+------------+---------+
But how do I get the last column?
Consider below approach
with your_table as (
select 1 as week, date '2022-02-07' as date, 1 as val union all
select 1, '2022-02-08', 2 union all
select 1, '2022-02-09', 3 union all
select 1, '2022-02-10', 4 union all
select 1, '2022-02-11', 5 union all
select 1, '2022-02-12', 6 union all
select 1, '2022-02-13', 7 union all
select 2, '2022-02-14', 8 union all
select 2, '2022-02-15', 9 union all
select 2, '2022-02-16', 10 union all
select 2, '2022-02-17', 11 union all
select 2, '2022-02-18', 12 union all
select 2, '2022-02-19', 13 union all
select 2, '2022-02-20', 14
), timeframe as (
select date '2022-02-14' as date_start, date '2022-02-17' as date_stop
)
select date_start, date_stop,
sum(if(date between date_start and date_stop,val, 0)) as val_cur,
sum(if(date between date_start - 7 and date_stop - 7,val, 0)) as val_prev
from your_table, timeframe
group by date_start, date_stop
with output
Tell me how to display lines as in the example through window functions.
The algorithm is as follows:
Group by “clusterid”, which is not null. And if “issuedate” and “operdate” are equal in each section, then we display all lines with “Publid” for which there is the largest number of unique combinations “publid + inn”.
Example
|*inn*|*publid*|*clusterid*|*issuedate*|*operdate*|
|-----|--------|-----------|-----------|----------|
| 333 | 1 | 12 | 01-01-21 | 05-01-21 |
| 222 | 1 | 12 | 01-01-21 | 05-01-21 |
| 333 | 2 | 12 | 01-01-21 | 05-01-21 |
| 222 | 2 | 12 | 01-01-21 | 05-01-21 |
| 111 | 2 | 12 | 01-01-21 | 05-01-21 |
|-----|--------|-----------|-----------|----------|
Result
|*inn*|*publid*|*clusterid*|*issuedate*|*operdate*|
|-----|--------|-----------|-----------|----------|
| 333 | 2 | 12 | 01-01-21 | 05-01-21 |
| 222 | 2 | 12 | 01-01-21 | 05-01-21 |
| 111 | 2 | 12 | 01-01-21 | 05-01-21 |
|-----|--------|-----------|-----------|----------|
I've been thinking about how to write the code for a long time, but I can't. There is the following idea, but not entirely correct.
SELECT a.*
FROM (SELECT m.*, RANK() OVER (PARTITION BY clusterid order by issuedate desc, operdate desc, count(inn) desc) AS rn
FROM table as m
GROUP BY publid
WHERE clusterid is not null
) AS a
WHERE a.rn = 1
This is how I understood it:
SQL> with test (inn, publid, clusterid, issuedate, operdate) as
2 (select 333, 1, 12, date '2021-01-01', date '2021-01-05' from dual union all
3 select 222, 1, 12, date '2021-01-01', date '2021-01-05' from dual union all
4 select 333, 2, 12, date '2021-01-01', date '2021-01-05' from dual union all
5 select 222, 2, 12, date '2021-01-01', date '2021-01-05' from dual union all
6 select 111, 2, 12, date '2021-01-01', date '2021-01-05' from dual
7 ),
8 temp as
9 (select inn, publid, clusterid, issuedate, operdate,
10 row_number() over (partition by clusterid, inn order by publid desc) rn
11 from test
12 )
13 select inn, publid, clusterid, issuedate, operdate
14 from temp
15 where rn = 1;
INN PUBLID CLUSTERID ISSUEDAT OPERDATE
---------- ---------- ---------- -------- --------
111 2 12 01.01.21 05.01.21
222 2 12 01.01.21 05.01.21
333 2 12 01.01.21 05.01.21
SQL>
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
i am trying to sum the qty and group by loc no , location,container name and qty but it is giving incorrect sum on the qty .. i have provided sample data that i created from table . any idea why sum with group by not working properly
select loc_no , location,container_name , sum(qty) as sum_qty
from table
group by loc_no,location,container_name
sample data below
Loc no location container name Qty
A abc 12344 12
A abc 2345 45
A abc 22WER 56
A abc WWRR 34
B xyz WWRR 24
B xyz WWRR 45
B xyz 12344 34
C rmz 12344 65
C rmz 12344 33
C rmz 32WE 24
D edc 32WE 34
D ced 12344 54
If you want the sum for various grouping sets then you can use CUBE:
SELECT CASE GROUPING( loc_no ) WHEN 1 THEN 'ANY' ELSE loc_no END
AS loc_no,
CASE GROUPING( location ) WHEN 1 THEN 'ANY' ELSE location END
AS location,
CASE GROUPING( container_name ) WHEN 1 THEN 'ANY' ELSE container_name END
AS container_name,
SUM( Qty ) AS total_qty
FROM table_name
GROUP BY CUBE( loc_no, location, container_name )
HAVING GROUPING( loc_no ) + GROUPING( location ) + GROUPING( container_name ) >= 2
Which, for your sample data:
CREATE TABLE table_name ( Loc_no, location, container_name, Qty ) AS
SELECT 'A', 'abc', '12344', 12 FROM DUAL UNION ALL
SELECT 'A', 'abc', '2345', 45 FROM DUAL UNION ALL
SELECT 'A', 'abc', '22WER', 56 FROM DUAL UNION ALL
SELECT 'A', 'abc', 'WWRR', 34 FROM DUAL UNION ALL
SELECT 'B', 'xyz', 'WWRR', 24 FROM DUAL UNION ALL
SELECT 'B', 'xyz', 'WWRR', 45 FROM DUAL UNION ALL
SELECT 'B', 'xyz', '12344', 34 FROM DUAL UNION ALL
SELECT 'C', 'rmz', '12344', 65 FROM DUAL UNION ALL
SELECT 'C', 'rmz', '12344', 33 FROM DUAL UNION ALL
SELECT 'C', 'rmz', '32WE', 24 FROM DUAL UNION ALL
SELECT 'D', 'edc', '32WE', 34 FROM DUAL UNION ALL
SELECT 'D', 'ced', '12344', 54 FROM DUAL;
Outputs:
LOC_NO | LOCATION | CONTAINER_NAME | TOTAL_QTY
:----- | :------- | :------------- | --------:
ANY | ANY | ANY | 460
ANY | ANY | 2345 | 45
ANY | ANY | 32WE | 58
ANY | ANY | WWRR | 103
ANY | ANY | 12344 | 198
ANY | ANY | 22WER | 56
ANY | abc | ANY | 147
ANY | ced | ANY | 54
ANY | edc | ANY | 34
ANY | rmz | ANY | 122
ANY | xyz | ANY | 103
A | ANY | ANY | 147
B | ANY | ANY | 103
C | ANY | ANY | 122
D | ANY | ANY | 88
db<>fiddle here
Hi guys I need your support how to perform this logic.
I'm stucking currently and i really do not know how to procide further.
Target: compare ref_num, entry_date and status=1
with
ref_num, change_status_date, status 0 (always the last two rows)
Compare, if these dates change_status_date and entry_date are less then 2 days old then update the status value from status=1 to status=2, else If the days are more then 3 days old change to status=0
Any idea how to perfom a correct select sql and update sql?
+--------------+-----------------------+---------------------+----------+
| ref_num | entry_date | change_status_date | status
+--------------+-----------------------+---------------------+----------+
| x326585 | 28/04/2020 16:54:14 | | 1 |
| x326585 | 25/04/2020 13:14:00 | 27/04/2020 23:44:00 | 0 |
| x326585 | 20/04/2020 11:15:02 | 20/04/2020 23:52:01 | 0 |
| A142585 | 28/04/2020 16:55:14 | | 1 |
| A142585 | 26/04/2020 11:54:04 | 27/04/2020 22:54:51 | 0 |
| A142585 | 24/04/2020 10:44:14 | 25/04/2020 13:17:23 | 0 |
| B188532 | 29/04/2020 11:34:41 | | 1 |
| B188532 | 14/04/2020 11:44:24 | 15/05/2020 23:11:10 | 0 |
| B188532 | 11/04/2020 08:34:10 | 13/05/2020 11:44:41 | 0 |
+--------------+-----------------------+---------------------+----------+
END RESULTS:
+--------------+-----------------------+---------------------+----------+
| ref_num | entry_date | change_status_date | status
+--------------+-----------------------+---------------------+----------+
| x326585 | 28/04/2020 16:54:14 | 27/07/2020 23:47:31 | 2 | is less than 3 days (28/04/2020 16:54:14 - 27/04/2020 23:44:00) -> status 2
| x326585 | 25/04/2020 13:14:00 | 27/04/2020 23:44:00 | 0 |
| x326585 | 20/04/2020 11:15:02 | 20/04/2020 23:52:01 | 0 |
| A142585 | 28/04/2020 16:35:58 | 27/07/2020 23:47:31 | 2 | is less than 3 days (28/04/2020 16:35:58 - 27/04/2020 22:54:51) -> status 2
| A142585 | 26/04/2020 11:54:04 | 27/04/2020 22:54:51 | 0 |
| A142585 | 24/04/2020 10:44:14 | 25/04/2020 13:17:23 | 0 |
| B188532 | 29/04/2020 11:34:41 | 27/07/2020 23:47:31 | 0 | is more than 3 days (29/04/2020 11:34:41 - 15/05/2020 23:11:10) -> status 0
| B188532 | 14/04/2020 11:44:24 | 15/05/2020 23:11:10 | 0 |
| B188532 | 11/04/2020 08:34:10 | 13/05/2020 11:44:41 | 0 |
+--------------+-----------------------+---------------------+----------+
select x.ref_num, x.entry_date, x.change_status_date, x.status from kl_table x
Thank you for your support and advice
This is how I understood the question.
Sample data:
SQL> with test (ref_num, entry_date, change_status_date, status) as
2 (select 'x3', to_date('28.04.2020 16:54', 'dd.mm.yyyy hh24:mi'), null , 1 from dual union all
3 select 'x3', to_date('25.04.2020 13:14', 'dd.mm.yyyy hh24:mi'), to_date('27.04.2020 23:44', 'dd.mm.yyyy hh24:mi'), 0 from dual union all
4 select 'x3', to_date('20.04.2020 11:15', 'dd.mm.yyyy hh24:mi'), to_date('20.04.2020 23:52', 'dd.mm.yyyy hh24:mi'), 0 from dual union all
5 --
6 select 'b1', to_date('29.04.2020 11:34', 'dd.mm.yyyy hh24:mi'), null , 1 from dual union all
7 select 'b1', to_date('14.04.2020 11:44', 'dd.mm.yyyy hh24:mi'), to_date('15.05.2020 23:11', 'dd.mm.yyyy hh24:mi'), 0 from dual union all
8 select 'b1', to_date('11.04.2020 08:34', 'dd.mm.yyyy hh24:mi'), to_date('13.05.2020 11:44', 'dd.mm.yyyy hh24:mi'), 0 from dual
9 ),
Max change_status_date for that ref_num whose status = 0; it'll be compared to entry_date
10 temp as
11 (select
12 a.ref_num,
13 a.entry_date,
14 a.change_status_date,
15 --
16 (select max(b.change_status_date)
17 from test b
18 where b.ref_num = a.ref_num
19 and b.status = 0
20 ) compare_change_status_date,
21 a.status
22 from test a
23 )
Finally: I presume that change_status_date (that was NULL) should be replaced by sysdate. Difference between those dates should be ABS to eliminate negative numbers.
24 select
25 t.ref_num,
26 t.entry_date,
27 --
28 nvl(t.change_status_date, sysdate) change_status_date,
29 --
30 case when t.status = 1 then
31 case when abs(t.entry_date - t.compare_change_status_date) < 2 then 2
32 when abs(t.entry_date - t.compare_change_status_date) > 3 then 0
33 end
34 else t.status
35 end status
36 from temp t
37 order by t.ref_num desc, t.entry_date desc;
RE ENTRY_DATE CHANGE_STATUS_DA STATUS
-- ---------------- ---------------- ----------
x3 28.04.2020 16:54 28.07.2020 08:21 2
x3 25.04.2020 13:14 27.04.2020 23:44 0
x3 20.04.2020 11:15 20.04.2020 23:52 0
b1 29.04.2020 11:34 28.07.2020 08:21 0
b1 14.04.2020 11:44 15.05.2020 23:11 0
b1 11.04.2020 08:34 13.05.2020 11:44 0
6 rows selected.
SQL>
If you want to update rows whose status = 1, code I posted above can be reused for e.g. MERGE:
merge into test a
using (with temp
as (select a.ref_num,
a.entry_date,
a.change_status_date,
--
(select max (b.change_status_date)
from test b
where b.ref_num = a.ref_num
and b.status = 0)
compare_change_status_date,
a.status
from test a)
select t.ref_num,
t.entry_date,
--
nvl (t.change_status_date, sysdate) change_status_date,
--
case
when t.status = 1
then
case
when abs (
t.entry_date - t.compare_change_status_date) <
2
then
2
when abs (
t.entry_date
- t.compare_change_status_date) > 3
then
0
end
else
t.status
end
status
from temp t) x
on ( a.ref_num = x.ref_num
and a.entry_date = x.entry_date)
when matched
then
update set a.status = x.status
where a.status = 1;
Your solution will look like this.
update [tablename] set status=2 where DATEDIFF(day, [tablename].entry_date, [tablename].change_status_date) < 2
update [tablename] set status=0 where DATEDIFF(day, [tablename].entry_date, [tablename].change_status_date) > 3
Thanks
Need hive query that calculates the date difference for consecutive records but for the same txn type and generate same number if the difference is less than 10 else generate new number.
Input table
+--------+----------+-------------+
| Txn_id | Txn_type | Txn_date |
+--------+----------+-------------+
| 1 | T100 | 26-Aug-2015 |
| 2 | T100 | 03-Nov-2015 |
| 3 | T100 | 05-Dec-2015 |
| 4 | T100 | 08-Dec-2015 |
| 5 | T100 | 25-Jan-2016 |
| 6 | T111 | 26-Jan-2016 |
| 7 | T200 | 02-Feb-2016 |
| 8 | T200 | 07-May-2016 |
| 9 | T200 | 12-May-2016 |
| 10 | T200 | 20-May-2016 |
+--------+----------+-------------+
Expected output
+--------+----------+-------------+--------+
| Txn_id | Txn_type | Txn_date | Number |
+--------+----------+-------------+--------+
| 1 | T100 | 26-Aug-2015 | 1 |
| 2 | T100 | 03-Nov-2015 | 2 |
| 3 | T100 | 05-Dec-2015 | 3 |
| 4 | T100 | 08-Dec-2015 | 3 |
| 5 | T100 | 25-Jan-2016 | 4 |
| 6 | T111 | 26-Jan-2016 | 1 |
| 7 | T200 | 02-Feb-2016 | 1 |
| 8 | T200 | 07-May-2016 | 2 |
| 9 | T200 | 12-May-2016 | 2 |
| 10 | T200 | 20-May-2016 | 2 |
+--------+----------+-------------+--------+
Not sure if "less than 10 days" means strict or non-strict inequality, but otherwise:
with
inputs ( txn_id, txn_type, txn_date ) as (
select 1, 'T100', to_date('26-Aug-2015', 'dd-Mon-yy') from dual union all
select 2, 'T100', to_date('03-Nov-2015', 'dd-Mon-yy') from dual union all
select 3, 'T100', to_date('05-Dec-2015', 'dd-Mon-yy') from dual union all
select 4, 'T100', to_date('08-Dec-2015', 'dd-Mon-yy') from dual union all
select 5, 'T100', to_date('25-Jan-2016', 'dd-Mon-yy') from dual union all
select 6, 'T111', to_date('26-Jan-2016', 'dd-Mon-yy') from dual union all
select 7, 'T200', to_date('02-Feb-2016', 'dd-Mon-yy') from dual union all
select 8, 'T200', to_date('07-May-2016', 'dd-Mon-yy') from dual union all
select 9, 'T200', to_date('12-May-2016', 'dd-Mon-yy') from dual union all
select 10, 'T200', to_date('20-May-2016', 'dd-Mon-yy') from dual
),
prep ( txn_id, txn_type, txn_date, ct ) as (
select txn_id, txn_type, txn_date,
case when txn_date < lag(txn_date) over (partition by txn_type
order by txn_date) + 10 then 0 else 1 end
from inputs
)
select txn_id, txn_type, txn_date,
sum(ct) over (partition by txn_type order by txn_date) as number_
from prep;
I used number_ as a column name; don't use reserved Oracle words for table or column names unless your life depends on it, and not even then.
Use a common table expression to mark the rows that have a difference of more than 10 days and then count those to get the new number.
with test_data as (
SELECT 1 txn_id, 'T100' txn_type, to_date('26-AUG-2015','DD-MON-YYYY') txn_date from dual union all
SELECT 2 txn_id, 'T100', to_date('03-NOV-2015','DD-MON-YYYY') from dual union all
SELECT 3 txn_id, 'T100', to_date('05-DEC-2015','DD-MON-YYYY') from dual union all
SELECT 4 txn_id, 'T100', to_date('08-DEC-2015','DD-MON-YYYY') from dual union all
SELECT 5 txn_id, 'T100', to_date('25-JAN-2016','DD-MON-YYYY') from dual union all
SELECT 6 txn_id, 'T111', to_date('26-JAN-2016','DD-MON-YYYY') from dual union all
SELECT 7 txn_id, 'T200', to_date('02-FEB-2016','DD-MON-YYYY') from dual union all
SELECT 8 txn_id, 'T200', to_date('07-MAY-2016','DD-MON-YYYY') from dual union all
SELECT 9 txn_id, 'T200', to_date('12-MAY-2016','DD-MON-YYYY') from dual union all
SELECT 10 txn_id, 'T200', to_date('20-MAY-2016','DD-MON-YYYY') from dual),
markers as (
select td.*,
case when td.txn_date - nvl(lag(td.txn_date)
over ( partition by txn_type order by txn_id ), td.txn_date-9999) > 10
THEN 'Y' ELSE NULL end new_txn_marker from test_data td )
SELECT txn_id, txn_type,txn_date,
count(new_txn_marker) over ( partition by txn_type order by txn_id ) "NUMBER"
FROM markers;