I am having an issue with this SQL code. I am trying to divide sum/ by the count of a date. I keep getting the grouping expressions sequence is empty, and 'TableBlank.rep_date' is not an aggregate function error. I have tried with ORDER BY and GROUP BY statements and I'm still getting the error.
SELECT
rep_date,
1 AS AssignRank,
StepCompSum,
'StepCompA' AS FunCat,
CAST( StepCompSum / COUNT( rep_date ) AS double ) AS ParticRate
FROM
TableBlank
WHERE
FunCat = 'StepCompA'
UNION
SELECT
rep_date,
2 AS AssignRank,
StepCompSum,
'StepCompB' AS FunCat,
CAST( StepCompSum / COUNT( rep_date ) AS double ) AS ParticRate
FROM
TableBlank
WHERE
FunCat = 'StepCompB'
UNION
SELECT
rep_date,
3 AS AssignRank,
StepCompSum,
'StepCompC' AS FunCat,
CAST( StepCompSum / COUNT( rep_date ) AS double ) AS ParticRate
FROM
TableBlank
WHERE
FunCat = 'StepCompC'
GROUP BY
rep_date,
StepCompSum
Difficult to know without sample data, but this might work:
SELECT
rep_date
, 2 AS AssignRank
, StepCompSum
, FunCat
, CAST(StepCompSum / (COUNT(rep_date) OVER (PARTITION BY FunCat)) AS double) AS ParticRate
FROM TableBlank
Although, I suspect what you are wanting from the CAST is to get the result to not be an integer. Here's what you'll get:
2 / 3 = 1 (because, integer math)
cast(1 as double) = 1.000
when you probably expect 0.667
You need to either cast the values first, or just include a value of the appropriate type in the expression:
StepCompSum * 1.0 / (COUNT(rep_date) OVER (PARTITION BY FunCat))
I Want to write in oracle sql, case over a computed column. The query is as follow.
Select trunc (Cost*100/Amt) as 'Computed_LTV',
Case
When Request_Term <19 THEN '60'
When Request_Term <=24THEN'-18'
When Computed_LTV <70.805 THEN '-20'
ELSE '-60'
END AS "Var_1"
FROM LOAN_APP
It is used only once, so I'd suggest you use the same formula:
SELECT TRUNC (Cost * 100 / Amt) AS Computed_LTV,
CASE
WHEN Request_Term < 19 THEN '60'
WHEN Request_Term <= 24 THEN '-18'
WHEN TRUNC (Cost * 100 / Amt) < 70.805 THEN '-20' --> here
ELSE '-60'
END AS Var_1
FROM LOAN_APP
If you insist on using a computed column, you'll have to use a CTE or an inline view, but that's probably a little bit too much in this simple case.
Also, get rid of double quotes in Oracle. They cause nothing but problems.
In order to sum several computed columns, do exactly that: sum them up. For example:
select trunc(cost * 100 / amt) as var1,
round(amount * price) as var2,
--
-- sum them using the "+" operator
(trunc(cost * 100 / amt) + round(amount * price)) as result
from ...
Or, if you computed them in a CTE, you'd then
with data as
(select trunc(cost * 100 / amt) as var1,
round(amount * price) as var2
from ...
)
-- now sum them up
select var1 + var2 as result
from data
You cannot re-use a column alias in the SELECT. But starting in Oracle 12C, you can use a lateral join to define it in the FROM clause:
SELECT x.Computed_LTV,
(Case When Request_Term < 19 THEN '60'
When Request_Term <= 24 THEN'-18'
When Computed_LTV < 70.805 THEN '-20'
ELSE '-60'
END) AS Var_1
FROM LOAN_APP la CROSS JOIN LATERAL
(SELECT TRUNC(Cost*100/Amt) as Computed_LTV FROM DUAL) x;
Note: Do not escape identifiers -- using either single quotes or double quotes. That just clutters queries, making them harder to write and to read.
I am trying to display last 60 minutes statistics on pentaho dashboard (oracle 11g query passing to pentaho ).
I have column (counter_buff) in my table with 1000 counter positions with sample data shown below
counter_buff= '0,8,9,3,2,6,....15,62' up to 1000 comma seperated values
i am trying to fetch each comma separated values from table as per fixed positions provided and sum them, so the problem is if i use multiple positions query gets bigger & slower, slower query result in delayed statistics on dashboard.
I created this sample query & result:
Query:
the Numbers showing in {} is a counter positions({16},{24}..), this positions will be user defined. query also using 6 union all same like this.
select * from
((SELECT MIN(to_char(TIMESTAMP,'HH24:MI:SS')) as TS,
'SELL' as "STATUS",
SUM((regexp_substr(counter_buff,'(.*?,){16}(.*?),', 1, 1,'', 2)) +
(regexp_substr(counter_buff,'(.*?,){24}(.*?),', 1, 1,'', 2)) +
(regexp_substr(counter_buff,'(.*?,){32}(.*?),', 1, 1,'', 2)) ......+
(regexp_substr(counter_buff,'(.*?,){168}(.*?),', 1, 1,'', 2))) AS "COUNTS"
FROM (SELECT * FROM SHOPS
order by TO_CHAR("TIMESTAMP",'YYYY-MM-DD HH24:MI:SS') desc) "SHOPS"
where TOY_NAME = 'LION'
and rownum <=60
GROUP BY TO_CHAR("TIMESTAMP",'HH24:MI'))
UNION ALL
(SELECT MIN(to_char(TIMESTAMP,'HH24:MI:SS')) as TS,
'RETURNED' as "STATUS",
SUM((regexp_substr(counter_buff,'(.*?,){17}(.*?),', 1, 1,'', 2)) +
(regexp_substr(counter_buff,'(.*?,){25}(.*?),', 1, 1,'', 2)) ..... +
(regexp_substr(counter_buff,'(.*?,){153}(.*?),', 1, 1,'', 2)) +
(regexp_substr(counter_buff,'(.*?,){161}(.*?),', 1, 1,'', 2)) +
(regexp_substr(counter_buff,'(.*?,){169}(.*?),', 1, 1,'', 2))) AS "COUNTS"
FROM (SELECT * FROM SHOPS
order by TO_CHAR("TIMESTAMP",'YYYY-MM-DD HH24:MI:SS') desc) "SHOPS"
where TOY_NAME = 'LION'
and rownum <=60
GROUP BY TO_CHAR("TIMESTAMP",'HH24:MI')) )
order by TS desc,STATUS desc
result:
this is just some rows of result, result will be as per query rowid (to reduce space i pasted half result only , but i am using data of 60 last minutes)
TS STATUS COUNTS
10:20:01 SELL 6
10:21:01 SELL 9
10:22:01 SELL 8
10:23:01 SELL 3
10:20:01 RETURNED 1
10:21:01 RETURNED 6
10:22:01 RETURNED 7
10:23:01 RETURNED 2
I am able to achieve my desired output, But i want faster & smaller query option.
I am new to oracle query
You should filter data as much as possible at first, then make rest of job. Also union is not needed, you can do everything in one grouping, then only unpivot result if needed.
Below two queries, which should be useful. In first you have to write regexp_substr as many times as needed:
/* sample data
with shops(toy_name, time_stamp, counter_buff) as (
select 'LION', timestamp '2018-07-27 13:15:27', '0,8,9,3,2,6,15,62' from dual union all
select 'BEAR', timestamp '2018-07-27 13:44:06', '7,3,9,3,3,6,11,39' from dual union all
select 'LION', timestamp '2018-07-27 16:03:09', '7,3,151,44,3,6,11,39' from dual union all
select 'LION', timestamp '2018-07-27 16:03:49', '7,3,11,4,3,6,11,39' from dual )
-- end of data */
select to_char(time_stamp, 'hh24:mi') ts,
sum(regexp_substr(counter_buff,'(.*?,){2}(.*?),', 1, 1,'', 2) +
regexp_substr(counter_buff,'(.*?,){5}(.*?),', 1, 1,'', 2)) sell,
sum(regexp_substr(counter_buff,'(.*?,){3}(.*?),', 1, 1,'', 2) +
regexp_substr(counter_buff,'(.*?,){6}(.*?),', 1, 1,'', 2)) retu
from (select time_stamp, counter_buff, row_number() over (order by time_stamp desc) rn
from shops where toy_name = 'LION') t
where rn <= 60
group by to_char(time_stamp, 'hh24:mi')
In second I join two tables of predefined numbers with your data. These are the "user defined positions" used next as parameter for regexp_substr.
with
/* sample data
shops(toy_name, time_stamp, counter_buff) as (
select 'LION', timestamp '2018-07-27 13:15:27', '0,8,9,3,2,6,15,62' from dual union all
select 'BEAR', timestamp '2018-07-27 13:44:06', '7,3,9,3,3,6,11,39' from dual union all
select 'LION', timestamp '2018-07-27 16:03:09', '7,3,151,44,3,6,11,39' from dual union all
select 'LION', timestamp '2018-07-27 16:03:49', '7,3,11,4,3,6,11,39' from dual ),
*/ -- end of sample data
sell as (select rownum rn, column_value cs from table(sys.odcinumberlist(2, 5)) ),
retu as (select rownum rn, column_value cr from table(sys.odcinumberlist(3, 6)) )
select *
from (
select sum(regexp_substr(counter_buff,'(.*?,){'||cs||'}(.*?),', 1, 1,'', 2)) sell,
sum(regexp_substr(counter_buff,'(.*?,){'||cr||'}(.*?),', 1, 1,'', 2)) retu, ts
from (select to_char(time_stamp, 'HH24:MI') ts, counter_buff
from (select * from shops where toy_name = 'LION' order by time_stamp desc)
where rownum <= 60)
cross join sell join retu using (rn) group by ts)
unpivot (val for status in (sell, retu))
In both queries I assumed that sell is in positions (2, 5), returned in positions (3, 6). Also try row_number() against rownum and check which is faster for you. In both cases data is hit only once, this should speed up calculations.
I ve got problem with decimal places with my select .
I would like to get odds in format 10.50, 1.53, 1.004, 1200.00
select to_char(1.004,'9999.00') from dual;
from this select i get 1.00 but i need 1.004 .
SELECT rtrim(to_char(10.51, '9999.999'), '0') FROM DUAL;
from this i get ok (10.51) for odds where last number is not 0.But when odd is 10.50 i get 10.5 which i dont want i need at least two decimal places after dot.
Is there possibility to get round at 2 decimal places but when i get number with 3 to show 3 decimal places..
You could use a regular expression to remove the last character only if it is a zero:
regexp_replace(to_char(<value>, '9999.999'), '0$', null)
Demo:
with your_table (odds) as (
select 10.50 from dual
union all select 1.53 from dual
union all select 1.004 from dual
union all select 1200.00 from dual
)
select odds, regexp_replace(to_char(odds, '9999.999'), '0$', null) as formatted
from your_table;
ODDS FORMATTED
---------- ---------
10.5 10.50
1.53 1.53
1.004 1.004
1200 1200.00
You could use normal string funcitons like instr and substr but it gets a bit messy here.
Or you could use a case expression to decide which format model to use:
to_char(<value>,
case when <value> = trunc(<value>, 2) then '9999.99'
else '9999.999' end)
or
case when <value> = trunc(<value>, 2) then to_char(<value>, '9999.99')
else to_char(<value>, '9999.999') end
or some variant of that (e.g. deciding just now many 9s to include).
Demo:
with your_table (odds) as (
select 10.50 from dual
union all select 1.53 from dual
union all select 1.004 from dual
union all select 1200.00 from dual
)
select odds,
case when odds = trunc(odds, 2) then to_char(odds, '9999.99')
else to_char(odds, '9999.999')
end as formatted
from your_table;
ODDS FORMATTED
---------- ---------
10.5 10.50
1.53 1.53
1.004 1.004
1200 1200.00
Try this
Select to_char(12.5, rtrim(lpad(' ',length(trunc(12.5))+1, '9'))||'.'||rtrim(lpad(' ', case when length(12.5 - trunc(12.5)) <= 2 Then 3 else length(12.5 - trunc(12.5)) end, '0'))) from dual
Note: change 12.5 to any number. it will return as per your requirement
we all know how to generate a running total column with
SELECT id, date, value, sum(value)
OVER (partition by id order by date) total
FROM dual
ORDER BY date
Which will give me something like
ID DATE VALUE TOTAL
1 1/1/14 0.001 0.001
2 2/1/14 0.003 0.004
3 3/1/14 0.002 0.006
Now I want to generate a "running multiplication" which generated 0.001 * 0.004 * 0.006. I know that if I just want the value for the whole multiplication can be done by something like
SELECT exp(sum(ln(value))) from dual
but this one does not work with the partition in oracle. Maybe someone has an idea?
Edit
The desired result would be (don't mind the numbers, they are just dummies, they will not run into an overflow).
ID DATE VALUE TOTAL
1 1/1/14 0.001 0.001
2 2/1/14 0.003 0,000004
3 3/1/14 0.002 0,000000024
The exp(sum(ln())) approach works as long as you add the analytics for the sum() part, not for the exp(). This would give you the product of the original values:
WITH t AS (
SELECT 1 AS id, DATE '2014-01-01' AS dat, 0.001 AS value FROM dual
UNION ALL SELECT 2, DATE '2014-01-02', 0.003 FROM dual
UNION ALL SELECT 3, DATE '2014-01-03', 0.002 FROM dual
)
SELECT id, dat, value, EXP(SUM(LN(value))
OVER (PARTITION BY null ORDER BY dat))
AS total
FROM t
ORDER BY dat;
ID DAT VALUE TOTAL
---------- --------- ---------- ----------
1 01-JAN-14 .001 .001
2 02-JAN-14 .003 .000003
3 03-JAN-14 .002 .000000006
And this would give you the product of the running total:
WITH t AS (
SELECT 1 AS id, DATE '2014-01-01' AS dat, 0.001 AS value FROM dual
UNION ALL SELECT 2, DATE '2014-01-02', 0.003 FROM dual
UNION ALL SELECT 3, DATE '2014-01-03', 0.002 FROM dual
),
u AS (
SELECT id, dat, value, SUM(value)
OVER (PARTITION BY null ORDER BY dat) AS total
FROM t
)
SELECT id, dat, value, total, EXP(SUM(LN(total))
OVER (PARTITION BY null ORDER BY dat)) AS product
FROM u
ORDER BY dat;
ID DAT VALUE TOTAL PRODUCT
---------- --------- ---------- ---------- ----------
1 01-JAN-14 .001 .001 .001
2 02-JAN-14 .003 .004 .000004
3 03-JAN-14 .002 .006 .000000024
Use your own table instead of the CTE obviously; and if you're trying to get the product/sum over multiple values with an ID when change it to partition by id. Using null is to make this work with your sample data.
Unashamedly riffing off this demonstration of a custom aggregate product() function, which supports windowing, you could create your own analytic function to do the calculation for you:
CREATE OR REPLACE TYPE product_total_impl AS OBJECT
(
product NUMBER,
total NUMBER,
product_total NUMBER,
STATIC FUNCTION ODCIAggregateInitialize(ctx IN OUT product_total_impl) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateIterate(SELF IN OUT product_total_impl,
VALUE IN NUMBER) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateMerge(SELF IN OUT product_total_impl,
ctx2 IN product_total_impl) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateTerminate(SELF IN OUT product_total_impl,
returnvalue OUT NUMBER,
flags IN NUMBER) RETURN NUMBER
);
/
CREATE OR REPLACE TYPE BODY product_total_impl IS
STATIC FUNCTION ODCIAggregateInitialize(ctx IN OUT product_total_impl) RETURN NUMBER IS
BEGIN
ctx := product_total_impl(1, 0, 1);
RETURN ODCIConst.Success;
END ODCIAggregateInitialize;
MEMBER FUNCTION ODCIAggregateIterate(SELF IN OUT product_total_impl,
VALUE IN NUMBER) RETURN NUMBER IS
BEGIN
IF VALUE IS NOT NULL THEN
SELF.product := SELF.product * VALUE;
SELF.total := SELF.total + VALUE;
SELF.product_total := SELF.product_total * SELF.total;
END IF;
RETURN ODCIConst.Success;
END ODCIAggregateIterate;
MEMBER FUNCTION ODCIAggregateMerge(SELF IN OUT product_total_impl,
ctx2 IN product_total_impl) RETURN NUMBER IS
BEGIN
SELF.product := SELF.product * ctx2.product;
SELF.total := SELF.total + ctx2.total;
SELF.product_total := ctx2.product_total * ctx2.total;
RETURN ODCIConst.Success;
END ODCIAggregateMerge;
MEMBER FUNCTION ODCIAggregateTerminate(SELF IN OUT product_total_impl,
returnvalue OUT NUMBER,
flags IN NUMBER) RETURN NUMBER IS
BEGIN
returnvalue := SELF.product_total;
RETURN ODCIConst.Success;
END ODCIAggregateTerminate;
END;
/
CREATE OR REPLACE FUNCTION product_total(x IN NUMBER) RETURN NUMBER
PARALLEL_ENABLE
AGGREGATE USING product_total_impl;
/
Then you can do:
WITH t AS (
SELECT 1 AS id, DATE '2014-01-01' AS dat, 0.001 AS value FROM dual
UNION ALL SELECT 2, DATE '2014-01-02', 0.003 FROM dual
UNION ALL SELECT 3, DATE '2014-01-03', 0.002 FROM dual
)
SELECT id, dat, value,
SUM(value) OVER (PARTITION BY null ORDER BY dat) AS total,
PRODUCT_TOTAL(value) OVER (PARTITION BY null ORDER BY dat) AS product_total
FROM t
ORDER BY dat;
ID DAT VALUE TOTAL PRODUCT_TOTAL
---------- --------- ---------- ---------- -------------
1 01-JAN-14 .001 .001 .001
2 02-JAN-14 .003 .004 .000004
3 03-JAN-14 .002 .006 .000000024
SQL Fiddle with the original product as well.
As before, use your own table instead of the CTE obviously; and if you're trying to get the product/sum over multiple values with an ID when change it to partition by id. Using null is to make this work with your sample data.