Oracle sql - Merging two tables with n periods each into one table - oracle

I am trying to merge two tables with n periods into one:
I have the below tables:
Tables
Period1 .. Period750 represents a date, eg, period 1 = Jan 1st, Period2, Jan 2nd ...
How can we get to that result ?
thank you for the advice,
regards,
Oscar

select
product, startdate
, sum(period1) as period1
, sum(period2) as period2
...
, sum(period750) as period750
from(
select * from table1 union all
select * from table2 union all
...
)
group by product, startdate

Use a MERGE statement:
MERGE INTO table1 t1
USING table2 t2
ON (t1.product = t2.product AND t1.stardate = t2.stardate)
WHEN MATCHED THEN
UPDATE
SET period1 = t1.period1 + t2.period1,
period2 = t1.period2 + t2.period2,
-- ...
period749 = t1.period749 + t2.period749,
period750 = t1.period740 + t2.period750
WHEN NOT MATCHED THEN
INSERT (
product,
stardate,
period1,
period2,
-- ...,
period749,
period750
) VALUES (
t2.product,
t2.stardate,
t2.period1,
t2.period2,
-- ...,
t2.period749,
t2.period750
);

Related

How to optimize a query that compares data from the same day for each month

I have a query that gets data from a given day and compares it with the same business day for each month (if it turns to be Sat, Sun, or holiday it should show the last day before i.e. if I say 25, it will return Sep 23, 2022, for September and Aug 25 for August, etc). DAILY_DATA table contains business days only, so this works:
SELECT *
FROM DAILY_DATA A, --It has partitions for every day, like 'PART_300922'
( SELECT TO_CHAR(DATE,'MM'),MAX(DATE) DATE
FROM DAILY_DATA
WHERE TO_CHAR(DATE,'YYYY')='2022'
AND TO_CHAR(DATE,'DD')<=TO_CHAR(TO_DATE(&query_date),'DD')
GROUP BY TO_CHAR(DATE,'MM')
)B
WHERE A.DATE = B.DATE
ORDER BY 1;
The problem is, it takes so long time cause it loops thorough the entire table, is there a way to optimize it? maybe using partitions or something?
Help please.
Try this:
WITH prep_daily_data(dat, dim, mdat) AS (
SELECT
d.dat,
TO_CHAR( d.dat , 'DD'),
TO_NUMBER(TO_CHAR(d.dat,'YYYYMM')) as mdat
FROM daily_data d
),
matching_dim(dat, next_dat) AS (
SELECT cm.dat, nm.dat
FROM prep_daily_data cm
JOIN prep_daily_data nm ON cm.dim = nm.dim AND cm.mdat+1 = nm.mdat
),
matching_others(dat, next_dat) AS (
SELECT dat, MAX(next_dat) FROM (
SELECT cm.dat AS dat, nm.dat AS next_dat
FROM prep_daily_data cm
JOIN prep_daily_data nm ON cm.dim > nm.dim AND cm.mdat+1 = nm.mdat
WHERE NOT EXISTS(SELECT 1 FROM matching_dim d WHERE d.dat = cm.dat)
)
GROUP BY dat
)
SELECT * FROM (
SELECT * FROM matching_dim
UNION ALL
SELECT * FROM matching_others
)
ORDER BY dat
;

Assign a Name to the Sub queries in Oracle

I have 2 sub queries which are repeated and are very long so I want to give it a name and then refer that name in the query as mentioned in https://stackoverflow.com/a/3058938/6700081.
I have a Customers table with ID, Name, OrderID
I have the Orders table with ID (this is the order ID so it is also the Foreign key), Price, Order_Date
This is my original query which is working fine:
SELECT CUSTOMERS.name, ORDERS.price FROM
CUSTOMERS INNER JOIN ORDERS
ON (CUSTOMERS.ORDER_ID = ORDERS.ID)
WHERE ORDERS.PRICE = (SELECT MAX(ORDERS.PRICE) from ORDERS where ORDERS.ORDER_DATE <= (SELECT ADD_MONTHS((SELECT MIN(ORDER_DATE) FROM ORDERS), 12*10) from ORDERS WHERE ROWNUM = 1))
AND ORDERS.ORDER_DATE <= (SELECT ADD_MONTHS((SELECT MIN(ORDER_DATE) FROM ORDERS), 12*10) from ORDERS WHERE ROWNUM = 1);
I tried to change it to a named query as below:
WITH MAX_ORDER_DATE as (SELECT ADD_MONTHS((SELECT MIN(ORDER_DATE) FROM ORDERS), 12*10) from ORDERS WHERE ROWNUM = 1),
WITH MAX_ORDER_PRICE as (SELECT MAX(ORDERS.PRICE) from ORDERS where ORDERS.ORDER_DATE <= (MAX_ORDER_DATE))
SELECT CUSTOMERS.name, ORDERS.price FROM
CUSTOMERS INNER JOIN ORDERS
ON (CUSTOMERS.ORDER_ID = ORDERS.ID)
WHERE ORDERS.PRICE = (MAX_ORDER_PRICE)
AND ORDERS.ORDER_DATE <= (MAX_ORDER_DATE);
But I get an error related to invalid table name. What is wrong with this query?
Your corrected query:
WITH MAX_ORDER_DATE AS (
SELECT ADD_MONTHS((SELECT MIN(ORDER_DATE) FROM ORDERS), 12*10) AS max_date
FROM ORDERS
WHERE ROWNUM = 1
),
MAX_ORDER_PRICE AS (
SELECT MAX(ORDERS.PRICE) AS max_price
FROM ORDERS
WHERE ORDERS.ORDER_DATE <= (SELECT max_date FROM MAX_ORDER_DATE)
)
SELECT c.name, o.price
FROM CUSTOMERS c
INNER JOIN ORDERS o
ON c.ORDER_ID = o.ID
WHERE
o.PRICE = (SELECT max_price FROM MAX_ORDER_PRICE) AND
o.ORDER_DATE <= (SELECT max_date FROM MAX_ORDER_DATE);
The main issues I noticed with your syntax were that you repeated WITH for each common table expression, when you only need to state it once at the beginning of the definitions. Also, if you want to use the single values you define in the two CTEs, you should use a subquery against those CTEs. Finally, I added aliases to the columns you select in the CTEs.

Systematic Random Sample - Teradata SQL Ast

I need to produce a systematic random sample of (approx) 2500 rows from (approx)230,000 rows with a unique auto generated number on each row.
Is this possible using Teradata SQL ast? (The Sample function produces a simple random sample.)
Thank you for your time.
When there's already a gapless unique row number:
select t.*
from mytable as t
cross join
( select random(1,2500) as rnd ) as dt -- random start row
where rownumber mod 2500 = rnd -- every 2500 rows
Otherwise ROW_NUMBER can be used to create it:
select t.*
from mytable as t
cross join
( select random(1,2500) as rnd ) as dt
qualify ROW_NUMBER() OVER (ORDER BY whatever_determines_your_order) mod 2500 = rnd
select rank() over(order by $primary_index_key), t1.*
FROM
(select * from $table_name
sample 2500) t1
Assistant would do that, and so would any other client.
Same approach can be used to generate winning Powerball numbers.
From your comment, I think that you cannot do it on the fly. You need to generate some table first that would contain Step# and step_value, for example:
[1,2500],[2,5000],...[x,x*2500]
Than you should join this table to your query and limit row numbers by the logic: rn = random_seed + step_value.
It will look like this:
select
from (
select t1.*
, row_number() over("ordering logic") as rn
from my_table
) as t1
, (select random(1,2500) as random_seed) as seed --so it will be generated only once
where exists (
select 1 from sampling_table as t2
where t1.rn = t2.step_value + seed.random_seed
)

Compare two tables in Hive without apply JOINS

I have 2 tables, TableA and TableB. Both having same set of columns C1, C2. Now need to compare both the table are having same DATA or NOT. How do you do without use JOIN. I tried MINUS operator ie.,
SELECT * FROM TableA
MINUS
SELECT * FROM TableB
But this is not supported in HIVE. May be impala has this SET operator?
Please suggest how to do without JOINS. Thanks.
You can try with
SELECT *
FROM T1
WHERE NOT EXISTS (SELECT * FROM T2 WHERE T1.X = T2.Y)
WHERE T1.X = T2.Y are the "key"
create table student
(
id integer,
subject string,
total_score integer
);
insert into student
(id, subject, total_score)
values
(1, 'math', 90);
insert into student
(id, subject, total_score)
values
(1, 'science', 100);
insert into student
(id, subject, total_score)
values
(2, 'math', 90);
insert into student
(id, subject, total_score)
values
(2, 'science', 80);
---------- MINUS ---------
select id,subject,
total_score
from ( select max (id) id,
subject,
total_score,
count (*)
from (
select *
from student
where id = 1
union all
select *
from student
where id = 2
) merged_data
group by subject, total_score
having count (*) = 1
) minus_data
where id is not null;
id subject total_score
2 science 80
1 science 100

conditional UPDATE in ORACLE with subquery

I have a table ( table1 ) that represents item grouping and another table ( table2 ) that represents the items themselves.
table1.id is foreign key to table2 and in every record of table1 I also collect information like the total number of records in table2 associated with that particular record and the sum of various fields so that I can show the grouping and a summary of what's in it without having to query table2.
Usually items in table2 are added/removed one at a time, so I update table1 to reflect the changes in table2.
A new requirement arose, choosen items in a group must be moved to a new group. I thought of it as a 3 step operation:
create a new group in table1
update choosen records in table2 to point to the newly created rec in table1
the third step would be to subtract to the group the number of records / the sum of those other fields I need do show and add them to the new group, data that I can find simply querying table2 for items associated with the new group.
I came up with the following statement that works.
update table1 t1 set
countitems = (
case t1.id
when 1 then t1.countitems - ( select count( t2.id ) from table2 t2 where t2.id = 2 )
when 2 then ( select count( t2.id ) from table2 t2 where t2.id = 2 )
end
),
sumitems = (
case t1.id
when 1 then t1.sumitems - ( select sum( t2.num ) from table2 t2 where t2.id = 2 )
when 2 then ( select sum( t2.num ) from table2 t2 where t2.id = 2 )
end
)
where t1.id in( 1, 2 );
is there a way to rewrite the statement without having to repeat the subquery every time?
thanks
Piero
You can use a cursor and a bulk collect update statement on the rowid. That way you can simply write the join query with the desired result and update the table with those values. I always use this function and make slight adjustments each time.
declare
cursor cur_cur
IS
select ti.rowid row_id
, count(t2.id) countitems
, sum(t2.num) numitems
from table t1
join table t2 on t1.id = t2.t1_id
order by row_id
;
type type_rowid_array is table of rowid index by binary_integer;
type type_countitems_array is table of table1.countitems%type;
type type_numitems_array is table of table1.numitems%type;
arr_rowid type_rowid_array;
arr_countitems type_countitems_array;
arr_numitems type_numitems_array;
v_commit_size number := 10000;
begin
open cur_cur;
loop
fetch cur_cur bulk collect into arr_rowid, arr_countitems, arr_numitems limit v_commit_size;
forall i in arr_rowid.first .. arr_rowid.last
update table1 tab
SET tab.countitems = arr_countitems(i)
, tab.numitems = arr_numitems(i)
where tab.rowid = arr_rowid(i)
;
commit;
exit when cur_cur%notfound;
end loop;
close cur_cur;
commit;
exception
when others
then rollback;
raise_application_error(-20000, 'ERROR updating table1(countitems,numitems) - '||sqlerrm);
end;

Resources