Condition in WHERE clause (Oracle) - sql

I need a query which returns data based on what month/year it is. Below is a subquery i wrote which returns one row - start_date and end_date are the values i need to use in my main query
WITH SUBQ AS (SELECT
dim.MONTH_NAME as current_month_name
,dim.year_period as current_month
,dim.PERIOD_YEAR as YEAR
,CASE WHEN dim.year_period NOT LIKE '%01' THEN to_number(CONCAT(to_char(dim.PERIOD_YEAR-1) , '01' ))
WHEN dim.year_period LIKE '%01'THEN to_number(CONCAT(to_char(dim.PERIOD_YEAR-2) , '01' ))
END AS START_DATE
,CASE WHEN dim.year_period NOT LIKE '%01' THEN to_number(CONCAT(to_char(dim.PERIOD_YEAR) , '01' ))
WHEN dim.year_period LIKE '%01'THEN to_number(CONCAT(to_char(dim.PERIOD_YEAR-1) , '01' )) END AS ENDDATE
from dim_periods dim WHERE dim.year_period=to_number(to_char(sysdate, 'YYYYMM')))
Question is - how do i use values from subquery with one row in where clauses?
I need to get something like this, i just dont understand how should i join my subquery and the rest of the tables i use -
select * from financial_data fd
where fd.year_period BETWEEN subq.start_date and subq.enddate

You could join the subquery, i.e. the CTE with the table, and then use the column name in the filter predicate. The result of the subquery in the WITH clause acts like a temporary table.
For example,
WITH SUBQ AS
(SELECT dim.MONTH_NAME AS current_month_name ,
dim.year_period AS current_month ,
dim.PERIOD_YEAR AS YEAR ,
CASE
WHEN dim.year_period NOT LIKE '%01'
THEN to_number(CONCAT(TO_CHAR(dim.PERIOD_YEAR-1) , '01' ))
WHEN dim.year_period LIKE '%01'
THEN to_number(CONCAT(TO_CHAR(dim.PERIOD_YEAR-2) , '01' ))
END AS START_DATE ,
CASE
WHEN dim.year_period NOT LIKE '%01'
THEN to_number(CONCAT(TO_CHAR(dim.PERIOD_YEAR) , '01' ))
WHEN dim.year_period LIKE '%01'
THEN to_number(CONCAT(TO_CHAR(dim.PERIOD_YEAR-1) , '01' ))
END AS ENDDATE
FROM dim_periods dim
WHERE dim.year_period=to_number(TO_CHAR(SYSDATE, 'YYYYMM'))
)
SELECT fd.COLUMNS,
q.COLUMNS
FROM financial_data fd
JOIN subq q
ON (fd.KEY = q.KEY) -- join key
WHERE fd.year_period BETWEEN q.start_date AND q.enddate;
So, SUBQ acts like a temporary table, which you join with financial_data table.
UPDATE OP doesn't want the ANSI join syntax.
WITH SUBQ AS
(SELECT dim.MONTH_NAME AS current_month_name ,
dim.year_period AS current_month ,
dim.PERIOD_YEAR AS YEAR ,
CASE
WHEN dim.year_period NOT LIKE '%01'
THEN to_number(CONCAT(TO_CHAR(dim.PERIOD_YEAR-1) , '01' ))
WHEN dim.year_period LIKE '%01'
THEN to_number(CONCAT(TO_CHAR(dim.PERIOD_YEAR-2) , '01' ))
END AS START_DATE ,
CASE
WHEN dim.year_period NOT LIKE '%01'
THEN to_number(CONCAT(TO_CHAR(dim.PERIOD_YEAR) , '01' ))
WHEN dim.year_period LIKE '%01'
THEN to_number(CONCAT(TO_CHAR(dim.PERIOD_YEAR-1) , '01' ))
END AS ENDDATE
FROM dim_periods dim
WHERE dim.year_period=to_number(TO_CHAR(SYSDATE, 'YYYYMM'))
)
SELECT fd.COLUMNS,
q.COLUMNS
FROM financial_data fd,
subq q
WHERE fd.KEY = q.KEY -- join key
AND fd.year_period BETWEEN q.start_date AND q.enddate;

Related

Concat/Union two tables in SQL

The sql query below creates two tables, tab1 with 3 columns (quarter, region and datasum) and tab2 with 2 columns (quarter and datasum).
I want to stack the values from tab1 and tab2 together (just like pd.concat([tab1, tab2]) in pandas/python). For that I need to create a new column in tab2 called region and insert that in the same position as the corresponding column in tab1. And after that I think I need to use UNION_ALL.
In tab2 I would like the value of the column region to be 'all' for every instance.
How could I achieve this?
I tried to use ALTER TABLE and ADD but I don't get that to work for me. Help would be much appreciated.
I work in SQL Oracle.
with base1 as(
select substr(municip,1,2) as region, data, age,
case when substr(time,6,2) in ('01','02','03') then substr(time, 1,4) || '_1'
when substr(time,6,2) in ('04','05','06') then substr(time, 1,4) || '_2'
when substr(time,6,2) in ('07','08','09') then substr(time, 1,4) || '_3'
else substr(time, 1,4) || '_4' end quarter
from sql_v1
where time >= '2021-01' and
),
base2 as(select data, age,
case when substr(time,6,2) in ('01','02','03') then substr(time, 1,4) || '_1'
when substr(time,6,2) in ('04','05','06') then substr(time, 1,4) || '_2'
when substr(time,6,2) in ('07','08','09') then substr(time, 1,4) || '_3'
else substr(time, 1,4) || '_4' end quarter
from sql_v1
where time >= '2021-01'),
tab1 as (select quarter, region,
sum (case when age between '16' and '64' then kvar else 0 end) datasum
from base
group by quarter, region
order by quarter, region),
tab2 as (select quarter,
sum (case when age between '16' and '64' then kvar else 0 end) datasum
from riket
group by quarter
order by quarter)
...select * from tab_union
It appears that you're using strings for storing dates??? Don't do that :(
If you use native datetime datatypes then you can extract the quarter using things like TO_CHAR(datetime_column, 'Q')
For now, however, using the horrendous datatypes, you can restructure your query to use ROLLUP in your GROUP BY...
WITH
formatted AS
(
SELECT
SUBSTR(municip,1,2) AS region,
SUBSTR(time, 1,4) || CASE WHEN SUBSTR(time,6,2) IN ('01','02','03') THEN '_1'
WHEN SUBSTR(time,6,2) IN ('04','05','06') THEN '_2'
WHEN SUBSTR(time,6,2) IN ('07','08','09') THEN '_3'
ELSE '_4' END AS quarter,
age,
kvar
FROM
sql_v1
WHERE
time >= '2021-01'
)
SELECT
region,
COALESCE(quarter, 'All'),
SUM(CASE WHEN age BETWEEN 16 AND 64 THEN kvar ELSE 0 END)
FROM
formatted
GROUP BY
region, ROLLUP(quarter)
ORDER BY
region,
GROUPING(quarter),
quarter
Demo : https://dbfiddle.uk/?rdbms=oracle_21&fiddle=ab27d71ac81bb9bc7e5e06e7f5a44ba9
Or, using DATE datatype : https://dbfiddle.uk/?rdbms=oracle_21&fiddle=1544406dc2b6669bc62ed02a6155b1c2

UNION ALL IS NOT WORKING WITH GROUP BY IN SQL

I have to create a query that shows whether the employees has taken what kind of leave on time card or if they have taken any leave "Vacation".
The output should look like:
SELECT person_number,
Sum(
CASE
WHEN element = 'Overtime' THEN measure
END) AS overtime_measure_hours,
Sum(
CASE
WHEN elements LIKE 'Regular Pay' THEN measure
END) AS regular_measure_hours,
Sum(
CASE
WHEN elements IN ( 'Double_Time' ) THEN measure
END) AS Other_amount,
Max(
CASE
WHEN elements IN ( 'Double_Time' ) THEN 'Double'
END) AS Other_code
(
SELECT papf.person_number,
atrb.attribute_category element,
atrb.measure measure_hours,
rec.start_date,
rec.end_date
FROM per_all_people_f papf,
hwm_tm_rec rec,
fusion.hwm_tm_rep_atrbs atrb,
fusion.hwm_tm_rep_atrb_usages ausage,
hwm_tm_statuses status
WHERE 1=1
AND atrb.tm_rep_atrb_id = ausage.tm_rep_atrb_id
AND ausage.usages_source_id = rec.tm_rec_id
AND ausage.usages_source_version = rec.tm_rec_version
AND status.tm_bldg_blk_id = rec.tm_rec_id
AND status.tm_bldg_blk_version = rec.tm_rec_version
AND rec.tm_rec_type IN ( 'RANGE',
'MEASURE' )
AND papf.person_number = '101928'
AND trunc (status.date_to) = to_date ('31/12/4712', 'DD/MM/YYYY')
--and atrb.attribute_category in( 'Overtime','Regular Pay', 'Double_Time')
AND trunc (sh21.start_time) BETWEEN trunc(:P_From_Date) AND trunc(:P_To_Date) )
GROUP BY person_number
UNION ALL
SELECT person_number,
sum(
CASE
WHEN element = 'Overtime' THEN measure
END) AS overtime_measure_hours,
sum(
CASE
WHEN elements LIKE 'Regular Pay' THEN measure
END) AS regular_measure_hours,
sum(
CASE
WHEN absence_name IN ( 'Vacation' ) THEN abs_duration
END) AS Other_amount,
max(
CASE
WHEN absence_name IN ( 'Vacation' ) THEN absence_name
END) AS Other_code
(
SELECT papf.person_number,
atrb.attribute_category element,
atrb.measure measure_hours,
rec.start_date,
rec.end_date,
abs.NAME absence_name,
abs.duration abs_duration
FROM per_all_people_f papf,
hwm_tm_rec rec,
fusion.hwm_tm_rep_atrbs atrb,
fusion.hwm_tm_rep_atrb_usages ausage,
hwm_tm_statuses status,
anc_absence_types_vl abs
WHERE 1=1
AND atrb.tm_rep_atrb_id = ausage.tm_rep_atrb_id
AND ausage.usages_source_id = rec.tm_rec_id
AND ausage.usages_source_version = rec.tm_rec_version
AND status.tm_bldg_blk_id = rec.tm_rec_id
AND status.tm_bldg_blk_version = rec.tm_rec_version
AND rec.tm_rec_type IN ( 'RANGE',
'MEASURE' )
AND papf.person_number = '101928'
AND trunc (status.date_to) = to_date ('31/12/4712', 'DD/MM/YYYY')
--and atrb.attribute_category in( 'Overtime','Regular Pay', 'Double_Time')
AND trunc (sh21.start_time) BETWEEN trunc(:P_From_Date) AND trunc(:P_To_Date)
AND abs.absence_type_id = atrb.absence_type_id )
GROUP BY person_number
Although these queries are working separately it is not working with group by on each subquery.
If I merge the two queries, the issue I am having is with Other_code & other_amount. These columns have values from two different tables.
How can I solve this?
Output should be returned for the two dates p_from_date- 01-Jan-2021 and p_to_date- 31-Jul-2021 as:
Person_Number Overtime_measure_hours Regular_Measure_hours Other_code Other_amount
101928 18 15 Double_Time 34
101928 18 15 Vacation 1
i.e. Overtime_measure_hours, Regular_Measure_hours and Other_amount should have the sum of these values.

Count sequence selected columns

I have query below, I want sequence result like the value of 'feb' will sum by jan and feb, value of 'mar' will sum by jan, feb and mar,... . Is there any way to get the result like that?
select A.location as location
, count(Case When SUBSTRING(A.base_date,5,2)='01' Then A.customer_no else null end) as "jan"
, count(Case When SUBSTRING(A.base_date,5,2)='02' Then A.customer_no else null end) as "feb"
....
, count(Case When SUBSTRING(A.base_date,5,2)='12' Then A.customer_no else null end) as "dec"
from table_income A group by A.location;
SQL is a much more effective language when you think in rows rather than columns (normalisation).
For example, having one row per month is much simpler...
SELECT
location,
SUBSTRING(base_date,5,2) AS base_month,
SUM(COUNT(customer_no))
OVER (
PARTITION BY location
ORDER BY SUBSTRING(base_date,5,2)
)
AS count_cust
FROM
table_income
GROUP BY
location,
SUBSTRING(base_date,5,2)
Side notes:
If your base_date is a string, it shouldn't be, use data-types relevant to the data
If your base_date is a date or timestamp, you should really use date/timestamp functions, such as EXTRACT(month FROM base_date).
You probably should also account for different years...
SELECT
location,
DATE_TRUNC('month', base_date) AS base_month,
SUM(COUNT(customer_no))
OVER (
PARTITION BY location, DATE_TRUNC('year', base_date)
ORDER BY DATE_TRUNC('month', base_date)
)
AS count_cust
FROM
table_income
GROUP BY
location,
DATE_TRUNC('month', base_date)
Try this :
SELECT A.location as location
, count(Case When SUBSTRING(A.base_date,5,2) in ('01') Then A.customer_no else null end) as "jan"
, count(Case When SUBSTRING(A.base_date,5,2) in ('01','02') Then A.customer_no else null end) as "feb"
....
, count(Case When SUBSTRING(A.base_date,5,2) in ('01','02',...'12') Then A.customer_no else null end) as "dec"
from table_income A group by A.location;

Is there a way we can enhance the below query in Oracle 12C

Is there anyway we can optimize the below query in Oracle 12c to give faster result.
TAB_C - is the main table -2947109424 count and we have index in place on TAB_C(act_id).But still it is taking 3 hours to run in production.
SELECT ta.email_addr_id
, ta.email_addr
, SUBSTR(ta.email_addr, INSTR(ta.email_addr, '#') + 1,
INSTR(ta.email_addr, '.', -1) - INSTR(ta.email_addr, '#')
- 1) AS email_domain
, mtr.max_email_response_date
, ta.add_date
, net.num_12mons
, net.num_3mons
, net.num_6mons
, net.num_9mons
, ta.hard_bounce_ind
, ta.email_addr_text_att_02 AS preference_birth_date
,ta.source AS source
FROM Tab_A ta
LEFT JOIN (
SELECT tr.email_addr_id
, tcX(tr.email_response_date) AS max_email_response_date
FROM Tab_B tr
where tr.email_response_date = 'NEW'
AND tr.update_source <> 'EXP_EVENT'
GROUP BY tr.email_addr_id
) mtr
ON ta.email_addr_id = mtr.email_addr_id
LEFT JOIN (
SELECT tc.email_addr_id
, COUNT(DISTINCT tc.m_act_id) AS num_12mons
, COUNT(DISTINCT CASE WHEN ROUND(tc.outbound_date, 'DD')
> (ROUND(sysdate, 'DD') - 90)
THEN tc.m_act_id ELSE NULL END) AS num_3mons
, COUNT(DISTINCT CASE WHEN ROUND(tc.outbound_date, 'DD')
> (ROUND(sysdate, 'DD') - 180)
THEN tc.m_act_id ELSE NULL END) AS num_6mons
, COUNT(DISTINCT CASE WHEN ROUND(tc.outbound_date, 'DD')
> (ROUND(sysdate, 'DD') - 270)
THEN tc.m_act_id ELSE NULL END) AS num_9mons
FROM Tab_C tc
INNtr JOIN act a
ON tc.act_id = a.act_id
where a.channel_code IN ('FM','RM')
AND ROUND(tc.outbound_date, 'DD') > (ROUND(sysdate, 'DD') - 365)
GROUP BY tc.email_addr_id
) net
ON ta.email_addr_id = net.email_addr_id
where ta.email_addr_id <> 0 ;
Any insight will be highly appreciated.

Hive end of the last month

INSERT OVERWRITE TABLE test_month
PARTITION (dt= LAST_DAY('${CURRENT_DATE}'))
SELECT '${CURRENT_DATE}', LAST_DAY('${CURRENT_DATE}');
Current date is first day of the month. I want to achieve something like above. It is not working. This will be HiveQL used in oozie.
Their may be couple ways, here is one way
select order_date, date_sub(concat(
(case
WHEN MONTH(order_date) = '12' THEN concat( (YEAR(order_date) +1) , '-01')
WHEN MONTH(order_date) >= '10' THEN concat( (YEAR(order_date)) , '-', (MONTH(order_date) +1))
WHEN MONTH(order_date) >= '1' THEN concat( (YEAR(order_date)) , '-0', (MONTH(order_date) +1))
ELSE 'XX' END) ,'-01' ) ,1)