I need to update a table with another table [duplicate] - oracle

I have two tables in the following structure:
TABLE 1:
ITEM | JAN | FEB | MAR | APR | MAY | JUN
___________________________________________
Item A| 50 | 10 | 25 | NULL| NULL| NULL
Item C| 26 | 20 | 23 | NULL| NULL| NULL
Item B| 25 | 30 | 22 | NULL| NULL| NULL
TABLE 2:
ITEM | JAN | FEB | MAR | APR | MAY | JUN
___________________________________________
Item A| NULL| NULL| NULL| 32 | 26 | 12
Item B| NULL| NULL| NULL| 25 | 24 | 10
Item D| NULL| NULL| NULL| 22 | 35 | 14
I am trying to merge the tables, to get the following result:
ITEM | JAN | FEB | MAR | APR | MAY | JUN
___________________________________________
Item A| 50 | 10 | 25 | 32 | 26 | 12
Item B| 25 | 30 | 22 | 25 | 24 | 10
Item C| 26 | 20 | 23 | NULL| NULL| NULL
Item D| NULL| NULL| NULL| 22 | 35 | 14
I tried the following query:
MERGE INTO TABLE1 a USING (
SELECT REBATE_ITEM, JAN, FEB, MAR, APR, MAY FROM TABLE2
) b
ON (TRIM(a.ITEM) = TRIM(b.ITEM) AND a.JUN is null)
WHEN MATCHED THEN
UPDATE SET
a.APR = b.APR,
a.MAY = b.MAY,
a.JUN = b.JUN
I get the following result: SQL Error: ORA-38104: Columns referenced in the ON Clause cannot be updated:
Any ideas on how I can accomplish this merge/join/whatever?

Give this a try, I think it should work for what you are trying to do.
MERGE INTO TABLE1 a USING (
SELECT ITEM, JAN, FEB, MAR, APR, MAY, JUN
FROM TABLE2) b
ON (a.ITEM = b.ITEM)
WHEN MATCHED THEN
UPDATE SET
a.APR = b.APR,
a.MAY = b.MAY,
a.JUN = b.JUN
WHERE a.JUN = null
WHEN NOT MATCHED THEN
INSERT (a.ITEM, a.JAN, a.FEB, a.MAR, a.APR, a.MAY, a.JUN)
VALUES (b.ITEM, b.JAN, b.FEB, b.MAR, b.APR, b.MAY, b.JUN);

If you just want to select the values (rather than actually changing data in the database) you can use a union and a group by:
select ITEM,
sum(JAN) as jan,
sum(FEB) as feb,
sum(MAR) as mar,
sum(APR) as apr,
sum(may) as may
sum(JUN) as jun
from (
select ITEM, JAN, FEB, MAR, APR, MAY, JUN
from table1
union all
select ITEM, JAN, FEB, MAR, APR, MAY, JUN
from table2
) t
group by item;

Related

SAS Hive SQL (Hadoop) version of Proc Transpose?

I was wondering if there is a version of 'Proc Transpose' in SAS Hive SQL (Hadoop) ?
Otherwise I can see the only other (long winded) way is creating a lot of separate tables to then join back together, which I'd rather avoid.
Any assistance most welcome!
Sample table to Transpose > Intention to put Month along the top of the table so the rates are split by month:
+------+-------+----------+----------+-------+
| YEAR | MONTH | Geog | Category | Rates |
+------+-------+----------+----------+-------+
| 2018 | 1 | National | X | 32 |
| 2018 | 1 | National | Y | 43 |
| 2018 | 1 | National | Z | 47 |
| 2018 | 1 | Regional | X | 52 |
| 2018 | 1 | Regional | Y | 38 |
| 2018 | 1 | Regional | Z | 65 |
| 2018 | 2 | National | X | 63 |
| 2018 | 2 | National | Y | 14 |
| 2018 | 2 | National | Z | 34 |
| 2018 | 2 | Regional | X | 90 |
| 2018 | 2 | Regional | Y | 71 |
| 2018 | 2 | Regional | Z | 69 |
+------+-------+----------+----------+-------+
Sample output:
+------+----------+----------+----+----+
| YEAR | Geog | Category | 1 | 2 |
+------+----------+----------+----+----+
| 2018 | National | X | 32 | 63 |
| 2018 | National | Y | 43 | 14 |
| 2018 | National | Z | 47 | 34 |
| 2018 | Regional | X | 52 | 90 |
| 2018 | Regional | Y | 38 | 71 |
| 2018 | Regional | Z | 65 | 69 |
+------+----------+----------+----+----+
The typical wallpaper SQL technique for transposing (or pivoting) is a group+transform to pivot case statements sub-query within a group aggregating query that collapses the sub-query. The group represents a single resultant pivot row.
For example your group is year, geog, category and min is used to collapse:
proc sql;
create view want_pivot as
select year, geog, category
, min(rate_m1) as rate_m1
, min(rate_m2) as rate_m2
from
( select
year, geog, category
, case when month=1 then rates end as rate_m1
, case when month=2 then rates end as rate_m2
from have
)
group by year, geog, category
;
Here is the same concept, a little more generically where data is repeated within the group at the detail level and mean is used to collapse over the repeats.
data have;
input id name $ value;
datalines;
1 a 1
1 a 2
1 a 3
1 b 2
1 c 3
2 a 2
2 d 4
2 b 5
3 e 1
run;
proc sql;
create view have_pivot as
select
id
, mean(a) as a
, mean(b) as b
, mean(c) as c
, mean(d) as d
, mean(e) as e
from
(
select
id
, case when name='a' then value end as a
, case when name='b' then value end as b
, case when name='c' then value end as c
, case when name='d' then value end as d
, case when name='e' then value end as e
from have
)
group by id
;
quit;
When the column names are not known apriori, you will need to write a code generator that passes over all the data to determine the name values, writes the wall paper query which will perform a second pass over the data returning the pivot.
Also, many contemporary data bases have a PIVOT clause that can be leveraged via pass through.
The Hadoop Mania post "TRANSPOSE/PIVOT a Table in Hive" shows the use of collect_list and map in a similar wallpapery manner:
select b.id, b.code, concat_ws('',b.p) as p, concat_ws('',b.q) as q, concat_ws('',b.r) as r, concat_ws('',b.t) as t from
(select id, code,
collect_list(a.group_map['p']) as p,
collect_list(a.group_map['q']) as q,
collect_list(a.group_map['r']) as r,
collect_list(a.group_map['t']) as t
from ( select
id, code,
map(key,value) as group_map
from test_sample
) a group by a.id, a.code) b;
if your sample dataset is representative of real dataset then you can use a simple inner join as shown below. Year geo and categoty makes unique combination below code should work.
select a.YEAR ,
a.Geog ,
a.Category ,
a.Rates ,
a.month as month_1,
b.month as month_2
from have a
inner join
have b
on a.year = b.year
and a.Geog = b.Geog
and a.Category = b.category
where a.month ne b.month;

Hive date format matching

How can i match particular date format in hive query, as i have to get those rows having date format other than max of rows.
Eg. My max of rows have date format as MM/dd/yyyy and i have to list all rows other than above format
+----------------------------+--------------------------+--------------------+-----------+
| AllocationActBankAccountID | GiftCardActBankAccountID | UpdateTimeStampUtc | Date |
+----------------------------+--------------------------+--------------------+-----------+
| 14 | 14 | 41:39.8 | 4/19/2016 |
| 14 | 14 | 37:16.4 | 4/20/2016 |
| 14 | 14 | 52:15.2 | 4/21/2016 |
| 14 | 14 | 52:15.2 | 2/11/2019 |
| 14 | 14 | 52:15.2 | 12-Feb-19 |*
| 14 | 14 | 41:39.8 | 2/13/2019 |
+----------------------------+--------------------------+--------------------+-----------+
I want to get * marked data (Date = 12-Feb-19)
select *
from mytable
Where date not rlike '^([1-9]|1[0-2])/([1-9]|[1-2][0-9]|3[0-1])/(19|20)\\d{2}$'
or
select *
from mytable
Where not
(
date rlike '^\\d{1,2}/\\d{1,2}/\\d{4}$'
and cast(split (date,'/')[0] as int) between 1 and 12
and cast(split (date,'/')[1] as int) between 1 and 31
and cast(split (date,'/')[2] as int) between 1900 and 2099
)
or
select date
from mytable
Where coalesce(from_unixtime(to_unix_timestamp(date,'M/d/y')),'0000-01-01')
not between date '1900-01-01' and date '2099-12-31'

Splitting char to multiple rows in Oracle

The table is as follows:
YEAR | MONTH | HOLIDAY |
2015 | 10 | # # # ### |
2015 | 11 | # # # # # |
I want to create the following:
YEAR | MONTH | DATE | VALUE |
2015 | 10 | 01 | # |
2015 | 10 | 02 | |
2015 | 10 | 03 | |
2015 | 10 | 04 | # |
UNTIL
2016 | 11 | 30 | # |
I've seen a few solutions around stack but It couldn't work
Any help would be greatly appreciating
Well, I'm not sure why you would want to go to November 31st, a date that doesn't exist. But, here is an idea:
with n as (
select level as n
from dual
connect by level <= 31
)
select t.year, t.month, n.n as day,
substr(t.holidays, n.n, 1) as holiday
from t join
n
on n.n <= length(t.holiday);

How to insert sum value to another table using procedure oracle

I want to make a table that contains summary value from another table; the purpose is to make a balance sheets report. I'm new in Oracle database so I'm still confused regarding how to do that. Is it using procedure, if yes so how to do it?
Here is the example data
In table 1 :
Year | Periode | Date Trx | Debit | Credit
2014 | Jan | 2/1/2014 | 50 | 0
2014 | Jan | 3/1/2014 | 0 | 20
2014 | Feb | 2/2/2014 | 0 | 100
2014 | Feb | 6/2/2014 | 50 | 0
2015 | Mar | 2/3/2014 | 0 | 80
2015 | Mar | 8/3/2014 | 50 | 0
2015 | Okt | 2/10/2014| 50 | 0
And I want the result in table 2 to like this:
Year | Periode | Debit | Credit
2014 | Jan | 50 | 20
2014 | Feb | 50 | 100
2015 | Mar | 50 | 80
2015 | Okt | 50 | 0
I hope someone could tell me how to make the procedure
from the sample that you provided - it can be resolved
via simple sql. it's common sql and no specific oracle features are required here
select year, period, sum(debit) as debit, sum(credit) as credit
from my_table
group by year, period
As #are notes a procedure is not needed here - but if you really insist on using one it would look something like
CREATE OR REPLACE PROCEDURE POPULATE_BALANCE_SHEET_REPORT IS
BEGIN
INSERT INTO TABLE2
SELECT YEAR, PERIODE, SUM(DEBIT), SUM(CREDIT)
FROM TABLE1
GROUP BY YEAR, PERIODE;
END POPULATE_BALANCE_SHEET_REPORT;
Best of luck.

List all days between two dates in Oracle

I am converting a postgres app to an Oracle app.
I came across this query:
WITH cost AS (SELECT
well_schedules.id,
generate_series(well_schedules.start_date::timestamp, well_schedules.end_date, '1 Day') AS "Date",
(well_schedules.drilling_engineering_estimate * well_schedules.well_estimated_working_interest)/((well_schedules.end_date - well_schedules.start_date) + 1) AS "Cost Per Day"
FROM
well_schedules
)
SELECT date_trunc('quarter', "Date"), COUNT("Cost Per Day"), id
FROM cost
GROUP BY id, date_trunc('quarter', "Date")
ORDER BY date_trunc('quarter', "Date")
The part I am struggling with is the generate_series line.
That line takes a start_date and end_date and lists all days between those two dates. We need that information to compile per day/week/month/quarter/year reports (or at least we assume we need that info).
Our data looks like this:
well_schedules
| id | start_date | end_date | cost |
| 1 | '2015-01-01' | '2015-03-20' | 100 |
We assume cost_per_day is equal across all days, so we'd like to generate a report that lets us look at cost_per_day, cost_per_week, cost_per_month, cost_per_year, and cost_per_quarter. cost_per_week/month/quarter/year is calculated by grouping the days by week/month/quarter/year and summing the associated cost_per_days
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE well_schedules ( id, start_date, end_date, cost ) AS
SELECT 1 , DATE '2015-01-01', DATE '2015-01-20', 100 FROM DUAL;
Query 1:
SELECT ID,
COLUMN_VALUE AS Day,
COST / ( end_date - start_date + 1 ) AS Cost_per_day
FROM well_schedules,
TABLE (
CAST(
MULTISET(
SELECT start_date + LEVEL - 1
FROM DUAL
CONNECT BY start_date + LEVEL - 1 <= end_date
)
AS SYS.ODCIDATELIST
)
)
Results:
| ID | DAY | COST_PER_DAY |
|----|---------------------------|--------------|
| 1 | January, 01 2015 00:00:00 | 5 |
| 1 | January, 02 2015 00:00:00 | 5 |
| 1 | January, 03 2015 00:00:00 | 5 |
| 1 | January, 04 2015 00:00:00 | 5 |
| 1 | January, 05 2015 00:00:00 | 5 |
| 1 | January, 06 2015 00:00:00 | 5 |
| 1 | January, 07 2015 00:00:00 | 5 |
| 1 | January, 08 2015 00:00:00 | 5 |
| 1 | January, 09 2015 00:00:00 | 5 |
| 1 | January, 10 2015 00:00:00 | 5 |
| 1 | January, 11 2015 00:00:00 | 5 |
| 1 | January, 12 2015 00:00:00 | 5 |
| 1 | January, 13 2015 00:00:00 | 5 |
| 1 | January, 14 2015 00:00:00 | 5 |
| 1 | January, 15 2015 00:00:00 | 5 |
| 1 | January, 16 2015 00:00:00 | 5 |
| 1 | January, 17 2015 00:00:00 | 5 |
| 1 | January, 18 2015 00:00:00 | 5 |
| 1 | January, 19 2015 00:00:00 | 5 |
| 1 | January, 20 2015 00:00:00 | 5 |
I will suggest the code below that consider the first and last day of the month from two dates:
Example:
Date Initial: 01/10/2014
Date Final: 12/21/2018
The code will return:
01/01/2014
02/01/2014
03/01/2014
04/01/2014
...
12/28/2018
12/29/2018
12/30/2018
12/31/2018
The Code:
SELECT
CAL.DT AS "Date"
,TO_NUMBER(TO_CHAR(CAL.DT,'DD')) AS "Day"
,TO_NUMBER(TO_CHAR(CAL.DT,'MM')) AS "Month"
,TO_NUMBER(TO_CHAR(CAL.DT,'YY')) AS "YearYY"
,TO_NUMBER(TO_CHAR(CAL.DT,'YYYY')) AS "YearYYYY"
,TO_CHAR(CAL.DT,'day') AS "Description_Day"
,TO_CHAR(CAL.DT,'dy') AS "Description_Day_Abrev"
,TO_CHAR(CAL.DT,'Month') AS "Description_Month"
,TO_CHAR(CAL.DT,'Mon') AS "Description_Month_Abrev"
,TO_CHAR(CAL.DT,'dd month yyyy') AS "Date_Text"
FROM (
SELECT
(
TO_DATE(SEQ.MM || SEQ.YYYY, 'MM/YYYY')-1
) + SEQ.NUM AS "DT"
FROM
(
SELECT RESULT NUM,
TO_CHAR(( -- Minimum Date
TO_DATE('01/01/2014', 'DD/MM/YYYY')
) , 'MM') AS "MM",
TO_CHAR(( -- Minimum Date
TO_DATE('01/01/2014', 'DD/MM/YYYY')
) , 'YYYY') AS "YYYY"
FROM
(
SELECT ROWNUM RESULT FROM DUAL CONNECT BY LEVEL <= (
(
-- Maximum Date
LAST_DAY(TO_DATE('31/12/2018', 'DD/MM/YYYY')) -- Always Last Day
-
-- Maximum Date
TRUNC(TO_DATE('01/01/2014', 'DD/MM/YYYY')) -- Always First Day of Month
) + 1 -- Because the First Day (RESULT) don't begin at zero
)
) -- How many sequences (RESULT) to generate
) SEQ
) CAL
;

Resources