Select timestamps for these values SQL - sql

Hello currently I have a working script below. I am using oracles 10
SELECT z.ID as "ID_One",
MAX(r.value) as "Max",
round(MAX(r.value)/80000,2) as "ROUND"
FROM Table1 r, Table2 z
WHERE r.timestamp > ((SYSDATE - TO_DATE('01/01/1970 00:00:00', 'MM-DD-YYYY HH24:MI:SS')) * 24 * 60 * 60) - 80000
AND r.id=21
AND r.animalid IN ('7','98','3','3')
AND r.id = z.id group by r.id, r.animalid,z.id;
It produces a table like this
ID_ONE MAX ROUND
1 500 232
2 232 32
3 23 .21
4 34 .321
I want to select a row call timestamp from table r. However when I add " r.timestamp " in to my query it produces 500 rows of data instead of 4. It looks like it is producing the the highest number for each timestamp instead. How would I produce a table that looks like this ? fyi timestamp column is in unix time. I can do the conversion myself. I just need to know how to get out these rows.
ID_ONE MAX ROUND TIMESTAMP
1 500 232 DEC 21,2021 10:00
2 232 32 DEC 21,2021 23:12
3 23 .21 DEC 31,2021 2:12
4 34 .321 DEC 31,2021 23:12

If you want other values from the row you're getting the MAX from, you can use analytic functions in a subquery instead of aggregates. There are various ways; this one ranks each row in the original tables such that the row with the highest value for each id is ranked first, and the outer query then only returns those.
SELECT id AS "ID_One",
value as "Max",
ROUND(value/80000,2) AS "ROUND",
timestamp,
TO_CHAR(DATE '1970-01-01' + (timestamp / (24 * 60 * 60)),
'MON DD,YYYY HH24:MI') AS timestamp_date
FROM (
SELECT z.id,
r.value,
r.timestamp,
RANK() OVER (PARTITION BY r.id, r.animalid ORDER BY r.value DESC) AS rnk
FROM Table1 r, Table2 z
WHERE r.timestamp > ((SYSDATE - DATE '1970-01-01') * 24 * 60 * 60) - 80000
--AND r.id=21
AND r.animalid IN ('7','98','3','3')
AND r.id = z.id
)
WHERE rnk = 1;
ID_One Max ROUND TIMESTAMP TIMESTAMP_DATE
---------- ---------- ---------- ---------- --------------------------
1 500 .01 1640080800 DEC 21,2021 10:00
2 232 0 1640128320 DEC 21,2021 23:12
3 23 0 1640916720 DEC 31,2021 02:12
4 34 0 1640992320 DEC 31,2021 23:12
SQL Fiddle, including the inner query on its own so you can see the ranking applied to each row.

Related

How to include values that count nothing on certain day (APEX)

I have this query:
SELECT
COUNT(ID) AS FREQ,
TO_CHAR(TRUNC(CREATED_AT),'DD-MON') DATES
FROM TICKETS
WHERE TRUNC(CREATED_AT) > TRUNC(SYSDATE) - 32
GROUP BY TRUNC(CREATED_AT)
ORDER BY TRUNC(CREATED_AT) ASC
This counts how many tickets where created every day for the past month.
The result looks something like this: (first 10 rows)
FREQ DATES
3 28-DEC
4 04-JAN
8 05-JAN
1 06-JAN
4 07-JAN
5 08-JAN
2 11-JAN
6 12-JAN
3 13-JAN
8 14-JAN
The linechart that I created looks like this:
The problem is that the days where tickets are not created (in particular the weekends) the line just goes straight to the day where there is created a ticket.
Is there a way in APEX or in my query to include the days that aren't counted?
As commented, using one of row generator techniques you'd create a "calendar" table and outer join it with a table that contains data you're displaying.
Something like this (see comments within code):
SQL> with yours (amount, datum) as
2 -- your sample table
3 (select 100, date '2021-01-01' from dual union all
4 select 200, date '2021-01-03' from dual union all
5 select 300, date '2021-01-07' from dual
6 ),
7 minimax as
8 -- MIN and MAX date (so that they could be used in row generator --> CALENDAR CTE (below)
9 (select min(datum) min_datum,
10 max(datum) max_datum
11 from yours
12 ),
13 calendar as
14 -- calendar, from MIN to MAX date in YOUR table
15 (select min_datum + level - 1 datum
16 from minimax
17 connect by level <= max_datum - min_datum + 1
18 )
19 -- final query uses outer join
20 select c.datum,
21 nvl(y.amount, 0) amount
22 from calendar c left join yours y on y.datum = c.datum
23 order by c.datum;
DATUM AMOUNT
---------- ----------
01.01.2021 100
02.01.2021 0
03.01.2021 200
04.01.2021 0
05.01.2021 0
06.01.2021 0
07.01.2021 300
7 rows selected.
SQL>
Applied to your current query:
WITH
minimax
AS
-- MIN and MAX date (so that they could be used in row generator --> CALENDAR CTE (below)
(SELECT MIN (created_at) min_datum, MAX (created_at) max_datum
FROM tickets),
calendar
AS
-- calendar, from MIN to MAX date in YOUR table
( SELECT min_datum + LEVEL - 1 datum
FROM minimax
CONNECT BY LEVEL <= max_datum - min_datum + 1)
-- final query uses outer join
SELECT COUNT (t.id) AS freq, TO_CHAR (TRUNC (c.datum), 'DD-MON') dates
FROM calendar c LEFT JOIN tickets t ON t.created_at = c.datum
WHERE TRUNC (t.created_at) > TRUNC (SYSDATE) - 32
GROUP BY TRUNC (c.datum)
ORDER BY dates ASC
I added a with clause to generate last 31 days, then I left joined with your base table like below.
with last_31_days as (
select trunc(sysdate) - 32 + level dt from dual connect by trunc(sysdate) - 32 + level < trunc(sysdate)
)
SELECT
nvl(COUNT(t.ID), 0) AS FREQ,
TO_CHAR(
nvl(TRUNC(t.CREATED_AT), a.dt)
,'DD-MON') DATES
FROM last_31_days a
LEFT JOIN TICKETS t
ON TRUNC(t.CREATED_AT) = a.dt
GROUP BY nvl(TRUNC(t.CREATED_AT), a.dt)
ORDER BY 2 ASC
;
#Littlefoot answer is perfect. but here is a cheeky way to get the similar table with format match OP output. using a simple cte for this.
WITH cte AS (
SELECT To_Char(Trunc(SYSDATE - ROWNUM),'DD-MON') dtcol
FROM DUAL
CONNECT BY ROWNUM < 366
)
SELECT * FROM cte
here is db<>fiddle
and then you can simply join this cte to fill up empty date. as the origin output column date looks like a string column.
connect by is for oracle only. but I think you can still use recursive cte to get similar result in other DBMS support recursive cte.

cumlative sum missing values of the month in sql

i have input data below
date amount
01-01-2020 10
01-02-2020 15
01-03-2020 10
01-05-2020 20
01-06-2020 30
01-08-2020 5
01-09-2020 6
01-10-2020 10
select sum(date),over(partition date) from table;
after add the missing month values i need output
output
Date amount cum_sum
01-01-2020 10 10
01-02-2020 15 25
01-03-2020 10 35
01-04-2020 0 35
01-05-2020 20 55
01-06-2020 30 85
01-07-2020 0 85
01-08-2020 5 90
01-09-2020 6 96
01-10-2020 10 106
You would typically generate the dates with a recursive query, then use window functions.
You don't tell which database you use. The exact syntax of recursive queries and date artithmetics varies across vendors, but here is what it would look like:
with recursive all_dates (dt, max_dt) as (
select min(date) dt, max(date) max_dt from mytable
union all
select dt + interval '1' day, max_dt from all_dates where dt < max_dt
)
select d.dt, sum(t.amount) over(order by c.dt) amount
from all_dates d
left join mytable t on t.date = d.dt
order by d.dt
You simply want a window function:
select t.*, sum(amount) over (order by date)
from table t

cumulative using case statement in Oracle's SQL

I have a simple data
Date Count by english count by chinese
08-Mar-19 12 54
09-Mar-19 15 66
10-Mar-19 45 32
11-Mar-19 21 70
12-Mar-19 57 64
29-Mar-19 43 53
30-Mar-19 67 21
I want to group this data by week and the sum should be cumulative.The date starts from 8 march so the week should be calculated that way only. So the result should be
count by english count by chinese
08-MAR-19-14-MAR-19 150 286
15-MAR-19-22-MAR-19 150 286 (no data so same as above)
23-MAR-19-30-MAR-19 260 360
Tried using cumulative and sum but not able to achieve it
You can generate your week ranges, then use an outer join to see which data fits in each week, and use an analytic sum to get the result you want;
with week_ranges (date_from, date_to) as (
select min_date + ((level - 1) * 7), min_date + (level * 7)
from (
select min(some_date) as min_date, ceil((max(some_date) - min(some_date)) / 7) as weeks
from your_table
)
connect by level <= weeks
)
select distinct wr.date_from, wr.date_to - 1 as date_to,
sum(count_english) over (order by wr.date_from) as count_english,
sum(count_chinese) over (order by wr.date_from) as count_chinese
from week_ranges wr
left join your_table yt
on yt.some_date >= wr.date_from
and yt.some_date < wr.date_to
order by date_from;
which with your sample data gets:
DATE_FROM DATE_TO COUNT_ENGLISH COUNT_CHINESE
---------- ---------- ------------- -------------
2019-03-08 2019-03-14 150 286
2019-03-15 2019-03-21 150 286
2019-03-22 2019-03-28 150 286
2019-03-29 2019-04-04 260 360
Note this is splitting it up into four 7-days weeks, rather than one of 7 days and two of 8 days...
db<>fiddle
Here's one option; note that "my weeks" are different than yours because - your data is somewhat inconsistent as they vary from 6 to 7 days. That's also why the final result is different, but the general idea should be OK.
SQL> alter session set nls_date_format = 'dd.mm.yyyy';
Session altered.
SQL> with test (datum, cbe) as
2 -- sample data
3 (select date '2019-03-08', 12 from dual union all
4 select date '2019-03-09', 15 from dual union all
5 select date '2019-03-10', 45 from dual union all
6 select date '2019-03-11', 21 from dual union all
7 select date '2019-03-12', 57 from dual union all
8 select date '2019-03-29', 43 from dual union all
9 select date '2019-03-30', 67 from dual
10 ),
11 span as
12 -- min and max date value, so that we could create a "calendar"
13 (select min(datum) mindat,
14 max(datum) maxdat
15 from test
16 ),
17 periods as
18 -- "calendar" whose periods are weeks
19 (select s.mindat + (level - 1) * 7 datum_from,
20 (s.mindat + level * 7) - 1 datum_to
21 from span s
22 connect by level <= (s.maxdat - s.mindat) / 7 + 1
23 )
24 -- running sum per weeks
25 select distinct
26 p.datum_from,
27 p.datum_to,
28 sum(t.cbe) over (order by p.datum_from) sum_cbe
29 from test t full outer join periods p on t.datum between p.datum_from and p.datum_to
30 order by p.datum_from;
DATUM_FROM DATUM_TO SUM_CBE
---------- ---------- ----------
08.03.2019 14.03.2019 150
15.03.2019 21.03.2019 150
22.03.2019 28.03.2019 150
29.03.2019 04.04.2019 260
SQL>

SQL how to split one row to multiple between two time?

I have a row like:
EMPID INTIME OUTTIME JOBCODE
1 4:00 5:00 ABC
2 5:00 8:00 ABC
Expected Output:
EMPID TIMEID JOBCODE MINUTE
1 16 ABC 15
1 17 ABC 15
1 18 ABC 15
1 19 ABC 15
TIMEID FOR 4:00 is 16 and increase with 15 minutes threshold.
I have tried with below query but it generates a single row.
SELECT
TO_NUMBER(SUBSTR(TO_CHAR(INTIME,'HH24:MI:SS'),0,2)* 4) + ROUND(TO_NUMBER((SUBSTR(TO_CHAR(INTIME,'HH24:MI:SS'),4,2)))/15,0) AS TIMEID,
EMPID,
JOBCODE,
MINUTE FROM MYTABLE;
Split 1 row like that as below:
with
x as
(select 1 Empid, to_date('01-JAN-1900 '||'4:00','DD-MON-YYYY HH24:MI') intime, to_date('01-JAN-1900 '||'5:12','DD-MON-YYYY HH24:MI') outtime from dual)
Select Empid,intime,(newtime-nvl(lag(newtime) over (order by newtime),intime))*1440 Required_Intervals,outtime,newtime
from (
select EmpId,InTime,Case When Outtime > INTIME+(15*rownum/1440) Then INTIME+(15*rownum/1440) Else Outtime End newTime,OUTTIME
from x
connect by rownum <= CEIL(((outtime-intime)*1440)/15))
1440 = 24 (hours per day) * 60 (minutes per hour) = total minutes per day
Please note it will work for only 1 row. For multiple rows as well you can create a query using multiset & connect by level if required.
The query also accounts for when the out-time is not in intervals of 15.

extracting total days of a month and then use it to get average sales per day

Hi i've been working on this project and need to get this.
SELECT sf.ORDER_QNT, dd.ACTUAL_DATE, dd.MONTH_NUMBER
FROM sales_fact sf,
date_dim dd
WHERE dd.date_id = sf.date_id
AND dd.MONTH_NUMBER = 1;
the result is the following:
ORDER_QNT ACTUAL_DATE MONTH_NUMBER
---------- ----------- ------------
1100 05/01/13 1
100 05/01/13 1
140 06/01/13 1
110 07/01/13 1
200 08/01/13 1
500 08/01/13 1
230 08/01/13 1
500 08/01/13 1
200 08/01/13 1
53 15/01/13 1
53 22/01/13 1
Now, I want to get the average for that month (average per day).
SELECT sum(sf.ORDER_QNT)/31 as AVGPERDAY
FROM sales_fact sf,
date_dim dd
WHERE dd.date_id = sf.date_id
AND dd.MONTH_NUMBER = 1;
The question is, instead of putting 31, how can I get the total day of the month? and how can I apply that to the SELECT query. I'm pretty good with logic(c++), but this database is pretty new to me. I'm using Oracle 11g by the way. Thank you for any help.
The question is, instead of putting 31, how can I get the total day of the month?
Pick any one solution :
1.
You can add a month to a date and substract both the dates :
ADD_MONTHS(date_col, 1) - date_col
Example :
SQL> WITH dates AS(
2 SELECT to_date('05/01/13','mm/dd/rr') dt FROM dual UNION ALL
3 SELECT to_date('06/01/13','mm/dd/rr') dt FROM dual UNION ALL
4 SELECT to_date('02/01/13','mm/dd/rr') dt FROM dual)
5 SELECT ADD_MONTHS(dt, 1) - dt num_of_days_per_month
6 from dates
7 /
NUM_OF_DAYS_PER_MONTH
---------------------
31
30
28
Or,
You can extract the last day of the month :
EXTRACT(DAY FROM LAST_DAY (date_col))
Example :
SQL> WITH dates AS(
2 SELECT to_date('05/01/13','mm/dd/rr') dt FROM dual UNION ALL
3 SELECT to_date('06/01/13','mm/dd/rr') dt FROM dual UNION ALL
4 SELECT to_date('02/01/13','mm/dd/rr') dt FROM dual)
5 SELECT EXTRACT(DAY FROM LAST_DAY(dt)) num_of_days_per_month
6 from dates
7 /
NUM_OF_DAYS_PER_MONTH
---------------------
31
30
28