I have procedure, which have one input parameter. I'm using this parameter to filter result table of query.
procedure gettabledata(level in number, cur out sys_refcursor) is
begin
open cur for
with data as (
select 'a' as field1, 'b' as field2, 'c' as field3, 'd' as field4, 10 as val from dual
union all
select 'a' as field1, 'b' as field2, 'd' as field3, 'c' as field4, 11 as val from dual
union all
select 'a' as field1, 'c' as field2, 'b' as field3, 'd' as field4, 12 as val from dual
union all
select 'a' as field1, 'c' as field2, 'd' as field3, 'b' as field4, 13 as val from dual
union all
select 'a' as field1, 'd' as field2, 'b' as field3, 'c' as field4, 14 as val from dual
union all
select 'a' as field1, 'd' as field2, 'c' as field3, 'b' as field4, 15 as val from dual
)
select null, null, null, field4, sum(val) from data where level = 1 group by field4
union all
select null, null, field3, field4, sum(val) from data where level = 2 group by field4, field3
union all
select null, field2, field3, field4, sum(val) from data where level = 3 group by field4, field3, field2
union all
select field1, field2, field3, field4, sum(val) from data where level = 4 group by field4, field3, field2, field1;
end;
It works fine, but I have a questiton. Are there any ways to filter result table with using case when else statement or do not use union statement?
Yes. You can use case when as following:
select case when level = 4 then field1 end,
Case when level >= 3 then field2 end,
case when level >= 2 then field3 end,
field4, sum(val) from data
group by
field4,
case when level >= 2 then field3 end,
Case when level >= 3 then field2 end
case when level = 4 then field1 end;
Cheers!!
Related
Consider the following sample table:
with v1 as (
select 'I' as parent_id, 'M' as child_id from dual union all
select 'M', 'M1' from dual union all
select 'M', 'D' from dual union all
select 'I', 'P' from dual union all
select 'P', 'K' from dual union all
select 'A', 'P1' from dual union all
select 'C', 'A' from dual union all
select 'A', 'I' from dual union all
select 'P1', 'K1' from dual
)
select ListAgg(child_id,'<-')
within group(order by Level desc) as Path
from v1
START WITH child_id = 'D'
CONNECT BY PRIOR parent_id = child_id;
It returns:
A<-I<-M<-D
It is not returning A's parent which is C.
What should I change in the query so that it returns C as well like below:
C<-A<-I<-M<-D
NOTE that before executing the query, I have no means to know that C is the root of the hierarchy. So, I can not pass C in the query (and that is how I have designed the my query above).
You can use following query:
With v1 as (
select 'C' as parent_id, 'A' as child_id from dual union all
select 'I', 'M' from dual union all
select 'M', 'M1' from dual union all
select 'M', 'D' from dual union all
select 'I', 'P' from dual union all
select 'P', 'K' from dual union all
select 'A', 'P1' from dual union all
select 'A', 'I' from dual union all
select 'P1', 'K1' from dual
)
select ListAgg(case when level =1 then parent_id || '<-' || child_id else parent_id end,'<-')
within group(order by Level desc) as Path
from v1
START WITH child_id = 'D'
CONNECT BY prior parent_id = child_id;
Sqlfiddle demo
Cheers!!
We have six hive tables with sample (example) structure like
(where each table has millions of merchant records)
Table1
MerchntId ,field1, field2
Table2
MerchantId, field3,field4
Table3
MerchantId, field5,field6,field7
Table4
MerchantId, field8,field9,field10
Table5
MerchantId, field11, field12, field13
and so on
Requirement is to create a horizantal layout to take all unique merchants where at least one field has value for a merchantId
A merchantId may or may not present in other tables.(for a merchant there may be records in other tables or may not be there)
Final Table
MerchntId, field1, field2, field3, field4,
field5,field6,field7,field8,field9,field10,field11, field12, field13
output should be like after joining
i) 101 abc def ghi
ii) 102 ghj fert hyu ioj khhh jjh ddd aas fff kkk fff vvv ff
for case (i) only three fields have values
for case (ii) all fields have values
For this we are doing FULL OUTER JOIN on merchantId for two tables and so on and then creating the final table
Is there any better approach doing this ?
for eg.
my current approach
SELECT distinct
(case when a.MerchntId IS NOT NULL then a.MerchntId else (case when
b.MerchntId IS NOT NULL
then b.MerchntId else '' end ) end ) as MerchntId,
(case when a.field1 IS NOT NULL then a.field1 else '' end ) as field1,
(case when a.field2 IS NOT NULL then a.field2 else '' end ) as field2,
(case when b.field3 IS NOT NULL then b.field3 else '' end ) as field3,
(case when b.field4 IS NOT NULL then b.field4 else '' end ) as field4
from Table1 a
full outer join Table2 b
ON a.MerchntId = c.MerchntId;
full outer join of table 3 and table 4
and then full outer join of these two tables to create a final table
I don't see any other option since your requirements explicitly translate to a full outer join. However, your query can be improved by using COALESCE and NVL:
SELECT
COALESCE(a.MerchntId, b.MerchntId) as MerchntId,
NVL(a.field1, '') as field1,
NVL(a.field2, '') as field2,
NVL(b.field3, '') as field3,
NVL(b.field4, '') as field4
from Table1 a
full outer join Table2 b
ON a.MerchntId = c.MerchntId;
Also, I'm not sure why you use distinct in your query.
Union all 6 table, substituting missed fields with nulls. Then Aggregate by MerchantId using min or max:
select MerchantId, max(field1) field1, max(field2) field2...max(field13) field13 from
(
select MerchntId field1, field2, null field3, null field4... null field13 from Table1
union all
select MerchntId null field1, null field2, field3, field4... null field13 from Table2
union all
...
select MerchantId, null field1, null field2... field11, field12, field13
from table6
)s group by MerchantId
After this you can apply your logic with replacing nulls with '' if necessary
Consider this part of my query:
SELECT field1, field2, field3, ...
LEFT JOIN (
SELECT field1, field2, MAX(field3) field3
FROM table
WHERE field2 IN ('1','2','3','4')
AND field4 > SYSDATE - 365
GROUP BY field1, field2) jointable ON other.fk= jointable.field1
So field4 is a date. I need the date from table. If I add it to the select list I must add it to the group by and as such it will no longer be grouped in a way to pull the MAX(field3).
I could join table again on their primary keys but that doesn't seem ideal. Is there a way to accomplish this?
You could use the aggregate keep dense_rank sytnax to get the date associated with the maximum field3 value for each field1/2 combination:
SELECT field1, field2, field3, ...
LEFT JOIN (
SELECT field1, field2, MAX(field3) field3,
MAX(field4) KEEP (DENSE_RANK LAST ORDER BY field3) field4
FROM table
WHERE field2 IN ('1','2','3','4')
AND field4 > SYSDATE - 365
GROUP BY field1, field2) jointable ON other.fk= jointable.field1
Quick demo of just the subquery, with a CTE for some simple data, where the highest field3 is not on the latest field4 date:
with your_table (field1, field2, field3, field4) as (
select 'A', '1', 1, date '2016-11-01' from dual
union all select 'A', '1', 2, date '2016-09-30' from dual
)
SELECT field1, field2, MAX(field3) field3,
MAX(field4) KEEP (DENSE_RANK LAST ORDER BY field3) field4
FROM your_table
WHERE field2 IN ('1','2','3','4')
AND field4 > SYSDATE - 365
GROUP BY field1, field2
/
F F FIELD3 FIELD4
- - ---------- ----------
A 1 2 2016-09-30
Seems like a window function would work well here...
SELECT field1, field2, field3, ...
LEFT JOIN (
SELECT field1, field2, MAX(field3) over (partition by field1, field2) field3, Field4
FROM table
WHERE field2 IN ('1','2','3','4')
AND field4 > SYSDATE - 365
GROUP BY field1, field2, field4) jointable ON other.fk= jointable.field1
Max of field 3 will now be independant of field 4 but still be dependant on fields 1 and 2.
The pivot function is available from Oracle 11 and i will need similar result using Oracle 9.2.
The main argument is that i need to pivot some values with a distinct result in a table like this:
id col3
1 a
1 b
--
2 a
2 a
2 b
--
3 a
3 b
3 c
My result sould be something like this
id a b c
1 1 1 0
2 1 1 0
3 1 1 1
To create a "manual" pivot i'm using a case/when but I am not able to understand how to get distinct value.
Right now the query is this:
with t as
( select 1 as id, 'a' as col1 from dual union all
select 1 as id, 'b' from dual union all
select 2 as id, 'a' from dual union all
select 2 as id, 'a' from dual union all
select 2 as id, 'b' from dual union all
select 3 as id, 'a' from dual union all
select 3 as id, 'b' from dual union all
select 3 as id, 'c' from dual
)
select t.id,
count(case when t.col1 = 'a' then 1 end) a,
count(case when t.col1 = 'b' then 1 end) b,
count(case when t.col1 = 'c' then 1 end) c
This produce correct value but obviously it just "count" the total a/b/c value and not the distinct.
thanks for the support
If I correctly understand your need, you can try something like the following; it aggregates by id and counts the distinct values of col3:
with t as
( select 1 as id, 'a' as col1 from dual union all
select 1 as id, 'b' from dual union all
select 2 as id, 'a' from dual union all
select 2 as id, 'a' from dual union all
select 2 as id, 'b' from dual union all
select 3 as id, 'a' from dual union all
select 3 as id, 'b' from dual union all
select 3 as id, 'c' from dual
)
select id,
count(distinct decode (col1, 'a', id, null)) a,
count(distinct decode (col1, 'b', id, null)) b,
count(distinct decode (col1, 'c', id, null)) c
from t
group by id
Of course the query depends on the number of different values of col3, but this is the same problem than pivot.
I need help with the following -- please help .
How to summate a date range.?? I am a newbie in Oracle .
Try something like this:
SELECT FIELD1, FIELD2, TRUNC(MIN(FIELD3)), TRUNC(MAX(FIELD3)), SUM(FIELD4)
FROM SOME_TABLE
WHERE FIELD3 BETWEEN DATE '2013-02-01'
AND DATE '2013-02-04' + INTERVAL '1' DAY - INTERVAL '1' SECOND
GROUP BY FIELD1, FIELD2
ORDER BY MIN(FIELD3);
Share and enjoy.
The questions/answers in the comment section to your ariginal answer show that you are actually looking for two different selections - one for the first date range and one for the overlapping second date range. Only you want to get all result records in a single result set. You can use UNION for that:
select field1, field2, min(trunc(field3))) || '-' || max(trunc(field3))), sum(field4)
from yourtable
where to_char(field3, 'yyyymmdd') between '20130201' and '20130204'
group by field1, field2
UNION
select field1, field2, min(trunc(field3))) || '-' || max(trunc(field3))), sum(field4)
from yourtable
where to_char(field3, 'yyyymmdd') between '20130203' and '20130207'
group by field1, field2
order by 1, 2, 3;
Using hard-coded dates is a bit odd, as is the way you're making your ranges (and your field4 value appears to be wrong in your sample output), but assuming you know what you want...
You can use acase statement to assign a dummy group number to the rows based on the dates, and then have an outer query that uses group by against that dummy field, which I've called gr:
select field1, field2,
to_char(min(field3), 'MM/DD/YYYY')
||'-'|| to_char(max(field3), 'MM/DD/YYYY') as field3,
sum(field4) as field4
from (
select field1, field2, field3, field4,
case when field3 between date '2013-02-01'
and date '2013-02-05' - interval '1' second then 1
when field3 between date '2013-02-05'
and date '2013-02-08' - interval '1' second then 2
end as gr
from t42
)
group by field1, field2, gr
order by field1, field2, gr;
F FIELD2 FIELD3 FIELD4
- ---------- --------------------- ----------
A 1 02/01/2013-02/04/2013 14
A 1 02/05/2013-02/07/2013 21
The display of field3 will look wrong if there is no data for one of the boundary days, but I'm not sure that's the biggest problem with this approach *8-)
You can potentially modify the case to have more generic groups, but I'm not sure how this will be used.
In a comment you say you specify two groups of dates which do not overlap. This comntradicts the data you posted in your question. Several people have wasted their time proposing non-solutions because of your tiresome inability to expalin your requirements in a clear and simple fashion.
Anyway, assuming you have finally got your story straight and the two groups don't overlap this should work for you:
with data as (
select field1
, field2
, field4
, case when field3 between date '2011-10-30' and date '2012-01-28' then 'GR1'
when field3 between date '2012-10-28' and date '2013-02-03' then 'GR2'
else null
end as GRP
from your_table )
select field1
, field2
, GRP
, sum(field4) as sum_field4
from data
where GRP is not null
order by 1, 2, 3
/