Oracle count with group by returning empty rows - oracle

I am trying to build a query where it returns the count for each month (passing a start and an end date) of a certain value, the output should be like this
Month Qtn
---------
|July|0|
|Augu|0|
|Sept|0|
but I get no rows, the query is the following:
SELECT TO_CHAR(TT_CUSTOMERS.CLOSED_DATE, 'YYYYMM') AS XMONTH, COUNT(TT_CUSTOMERS.NAMES) AS QTY FROM TT_CUSTOMERS
WHERE COMPANY = 700
AND TT_CUSTOMERS.C1_LOOKUP_ID = 100
AND TT_CUSTOMERS.C2_LOOKUP_ID = 1
AND TT_CUSTOMERS.C3_LOOKUP_ID IN (70, 80)
AND TT_CUSTOMERS.ST_LOOKUP_ID = 90
AND TO_NUMBER(TO_CHAR(TT_CUSTOMERS.CLOSED_DATE, 'YYYYMMDD')) >= TO_NUMBER('20170701')
AND TO_NUMBER(TO_CHAR(TT_CUSTOMERS.CLOSED_DATE, 'YYYYMMDD')) <= TO_NUMBER('20170930')
GROUP BY TO_CHAR(TT_CUSTOMERS.CLOSED_DATE, 'YYYYMM')
ORDER BY TO_CHAR(TT_CUSTOMERS.CLOSED_DATE, 'YYYYMM') ASC;
I know it's because the query is returning no rows, so the XMONTH is emtpy and so also the qty is empty, but I can't figure out a way to get the output that I've shown.

Assuming closed_date is of a date datatype...
and assuming all your other fields values are int and matches exist in the database...
I'd assume it's the numeric comparison of date values. Compare a date as a date not as numbers.
SELECT TO_CHAR(TT_CUSTOMERS.CLOSED_DATE, 'YYYYMM') AS XMONTH
, COUNT(TT_CUSTOMERS.NAMES) AS QTY
FROM TT_CUSTOMERS
WHERE COMPANY = 700
AND TT_CUSTOMERS.C1_LOOKUP_ID = 100
AND TT_CUSTOMERS.C2_LOOKUP_ID = 1
AND TT_CUSTOMERS.C3_LOOKUP_ID IN (70, 80)
AND TT_CUSTOMERS.ST_LOOKUP_ID = 90
AND TT_CUSTOMERS.CLOSED_DATE >= TO_DATE('20170701','YYYYMMDD')
AND TT_CUSTOMERS.CLOSED_DATE <= TO_DATE('20170930','YYYYMMDD')
GROUP BY TO_CHAR(TT_CUSTOMERS.CLOSED_DATE, 'YYYYMM')
ORDER BY TO_CHAR(TT_CUSTOMERS.CLOSED_DATE, 'YYYYMM') ASC;

Related

Oracle AVG COUNT

I have this query:
SELECT TRUNC(date_added,'MM'), count(*)
FROM payments_log l, product p
WHERE l.amount > 0
AND l.product_id = p.product_id
AND p.subproduct_id = 238
AND TRUNC(l.date_added) BETWEEN TO_DATE('01012020','MMDDYYYY') AND TO_DATE('01012021','MMDDYYYY')
AND l.return_code = 1
GROUP BY TRUNC(date_added,'MM')
ORDER BY TRUNC(date_added,'MM');
In addition to the count, per month, I want a column that is the average each month, of the total......not sure how to do this in the same query.
To get:
I want a column that is the average each month, of the total
You appear to want to use the AVG analytic function over the entire range:
SELECT month,
cnt,
AVG( cnt ) OVER () AS avg_cnt
FROM (
SELECT TRUNC(date_added,'MM') AS month,
COUNT(*) AS cnt
FROM payments_log l
INNER JOIN product p
ON ( l.product_id = p.product_id )
WHERE l.amount > 0
AND p.subproduct_id = 238
AND l.date_added >= DATE '2020-01-01'
AND l.date_added < DATE '2021-01-01'
AND l.return_code = 1
GROUP BY TRUNC(date_added,'MM')
)
ORDER BY month
You also should use ANSI joins rather than the (confusing) legacy comma joins and can filter on the date_added column without needing the TRUNC function (which, if you do use it, would prevent Oracle from using an index on the date_added column and would require a function-based index on TRUNC( date_added )).
(Note: BETWEEN is inclusive so that you will include 2021-01-01 in your range rather than just those dates in 2020; I am assuming that you do not want this date but if you do then you can set the upper bound to l.date_added < DATE '2021-01-02'.)
If you want the number of counts per month as a fraction of the total number of counts (this is not an average) then, again, you want to use an analytic function:
SELECT month,
cnt,
cnt / SUM( cnt ) OVER () AS fraction_of_total_cnt
FROM (
SELECT TRUNC(date_added,'MM') AS month,
COUNT(*) AS cnt
FROM payments_log l
INNER JOIN product p
ON ( l.product_id = p.product_id )
WHERE l.amount > 0
AND p.subproduct_id = 238
AND l.date_added >= DATE '2020-01-01'
AND l.date_added < DATE '2021-01-01'
AND l.return_code = 1
GROUP BY TRUNC(date_added,'MM')
)
ORDER BY month

PostgresSql max dates last 30 dates

In my dataset I have a list dates
How do I tag the latest 30 dates as "max"?
Col A: Dates
Col B: Values
Col C: Tag as "Max" or "NA"
I am making the assumptions that you don't know how recent to the current date the set will be or if it contains sparse dates. I don't have a running Postgres instance at the moment but this should be extremely close.
SELECT
t.ColA
, t.ColB
, CASE WHEN t2.ColA IS NOT NULL THEN 'Max' ELSE 'NA' END AS ColC
FROM TableName t
LEFT OUTER JOIN (SELECT DISTINCT inner_t.ColA
FROM TableName inner_t
ORDER BY 1 DESC
LIMIT 30) t2
ON t.ColA = t2.ColA

Datepart function in oracle

I have some sample records in Oracle 12
Date_Time Item
10/1/2012 12:05:00 AM 3
12/3/2012 06:00:00 AM 2
11/8/2012 14:05:05 PM 10
12/9/2012 16:00:59 PM 5
I like to aggregate the Item field based on military time or in three different times: 00:00:00AM to 05:59:00AM, 06:00:00AM to 15:59:00PM, and 16:00:00PM to 23:59:00PM. I was able to use the Datepart function in SQL to do this. I was wondering what function in Oracle 12 that allows me to count the Item between these three different times.
My desired output would be:
Date_Time Count
00:00:00AM to 05:59:00AM = 3
06:00:00AM to 15:59:00PM = 12
16:00:00PM to 23:59:00PM = 5
In oracle, date datatype contains date+time ,so you just need just use group by
SELECT Date_Time, COUNT(*) item FROM YOUR_TABLES
GROUP BY Date_Time;
NEW Answer:
SELECT TO_CHAR(DATE_TIME,'HH24') time, count(*) FROM YOUR_TABLE
WHERE TO_CHAR(DATE_TIME,'HH24') >= '00'
AND TO_CHAR(DATE_TIME,'HH24') < '06'
GROUP BY TO_CHAR(DATE_TIME,'HH24')
UNION ALL
SELECT TO_CHAR(DATE_TIME,'HH24') time, count(*) FROM YOUR_TABLE
WHERE TO_CHAR(DATE_TIME,'HH24') >= '06'
AND TO_CHAR(DATE_TIME,'HH24') < '16'
GROUP BY TO_CHAR(DATE_TIME,'HH24')
UNION ALL
SELECT TO_CHAR(DATE_TIME,'HH24') time, count(*) FROM YOUR_TABLE
WHERE TO_CHAR(DATE_TIME,'HH24') >= '16'
AND TO_CHAR(DATE_TIME,'HH24') < '00'
GROUP BY TO_CHAR(DATE_TIME,'HH24');
and if your table is huge :
first :partition it
second: create local functional index on TO_CHAR(DATE_TIME,'HH24:MI:SS')
Assuming that date_time column is datatype DATE, we can use the TO_CHAR function to extract a two character representation... in the range 00 to 23.
(The selected answer demonstrates this approach to extracting the "hour" from an Oracle DATE.)
Assuming that we want every non-null time value to fall into one of three time ranges... that is, if we don't want any of time values to be omitted because of a crack/gap in between the ranges, and we don't want any overlap in the ranges...
We can use a simple "less than" tests in a CASE expression.
Consider a time close to a boundary: '05:59:33'. That's after 05:59:00 but before 06:00:00. If we want that included in the first range, we can just test for hour < '06'.
If was grouping the rows into three ranges, and I wanted a total of the item column, I'd do something like this:
SELECT CASE
WHEN TO_CHAR( t.date_time ,'HH24') < '06' THEN '00:00:00 to 05:59:59'
WHEN TO_CHAR( t.date_time ,'HH24') < '16' THEN '06:00:00 to 15:59:59'
WHEN TO_CHAR( t.date_time ,'HH24') < '24' THEN '16:00:00 to 23:59:59'
END AS time_range
, SUM(t.item)
FROM mytable t
GROUP
BY CASE
WHEN TO_CHAR( t.date_time ,'HH24') < '06' THEN '00:00:00 to 05:59:59'
WHEN TO_CHAR( t.date_time ,'HH24') < '16' THEN '06:00:00 to 15:59:59'
WHEN TO_CHAR( t.date_time ,'HH24') < '24' THEN '16:00:00 to 23:59:59'
END
and add an ORDER BY clause if I want the results returned in a particular order.
If the table contains any NULL values of date_time, the query above will also return a fourth time_range with a NULL value.
Here is how you would get the desired result in three columns (rather than three rows), which makes more sense for most applications. You can change this easily to get the result in rows instead.
Note that if dt is any date in Oracle, dt - trunc(dt) is the number of days (a fraction with value less than 1) since midnight.
select sum(case when dt-trunc(dt) < 6/24 then item else 0 end) as morning,
sum(case when dt-trunc(dt) >= 6/24
and dt-trunc(dt) < 16/24 then item else 0 end) as daytime,
sum(case when dt-trunc(dt) >= 16/24 then item else 0 end) as evening
from your_table
;

Oracle Sql to "create" records based on fields

In Oracle I have a table with 5 fields:
ID
PublishDate
StartDate
EndDate
Value
The idea here is that I want to 'generate' records that are between StartDate and EndDate with the same values as the ID, PublishDate, & Value of those records.
ie this
10, 11/20/2014, 1/1/2016, 3/1/2016, 10
turns into
10, 11/20/2014, 1/1/2016, 10
10, 11/20/2014, 2/1/2016, 10
10, 11/20/2014, 3/1/2016, 10
So I've done this in a proc where I'm looping over each record and generating records, but I'm wondering if there is a SQL way to do something like this?
You can use CONNECT BY clause for hierarchical queries:
select * from (select 10 id, '11/20/2014' PublishDate, '1/1/2016' StartDate, '3/1/2016' EndDate, 10 value
from dual connect by level <= 10);
CONNECT BY level <= N will produce N copies of each row of the table
For your case:
SELECT ID, PublishDate, StartDate + lvl, VALUE
FROM your_tab
JOIN (SELECT LEVEL - 1 lvl FROM dual
CONNECT BY LEVEL <= (SELECT round(MAX(trunc(EndDate) - trunc(StartDate))) FROM your_tab)
)
ON trunc(EndDate) - trunc(StartDate) >= lvl

Oracle Rows into Columns

Can someone help me design the query to get below o/p.
Data I have:
Table X:
Count Name Amount Days
5 ABC 500 Day1
10 ABC 1000 Day2
3 BCD 100 Day1
4 BDC 200 Day2
Result I need:
Name Count AmountDay1 Count AmountDay2
ABC 5 500 10 1000
BCD 3 100 4 200
etc
Is this Possible?
I tried something with the below query, but not getting the desired o/p
select * from X
pivot
(sum(amount) for days in ('Day1', 'Day2'))
Please help
I'm using Oracle 11 G
Since you want to pivot on two columns Count and Amount, in might be easier to use an aggregate function with a CASE expression to get the result:
select name,
sum(case when days = 'Day1' then "count" else 0 end) CountDay1,
sum(case when days = 'Day1' then amount else 0 end) AmountDay1,
sum(case when days = 'Day2' then "count" else 0 end) CountDay2,
sum(case when days = 'Day2' then amount else 0 end) AmountDay2
from tableX
group by name;
See SQL Fiddle with Demo
If you want to use the PIVOT function, then you will want to unpivot the two columns Amount and Count, first:
select name, CountDay1, AmountDay1, CountDay2, AmountDay2
from
(
select name, col||days as col, value
from tableX
unpivot
(
value
for col in ("Count", Amount)
) u
) d
pivot
(
sum(value)
for col in ('CountDay1' as CountDay1, 'AMOUNTDay1' as AmountDay1,
'CountDay2' as CountDay2, 'AMOUNTDay2' as AmountDay2)
) piv;
See SQL Fiddle with Demo
you need to pivot on two columns count and amount:
select * from x
PIVOT
( min(count) as count ,sum(amount)for days in ('Day1', 'Day2'))

Resources