Insert Into Third Table Based Two Another- oracle - sql

I have these three tables; how can I insert into Table C (c_table) FROM b_table and a_table assuming that the volume of table's (especially A) data is very large:
Table B:
CREATE TABLE b_table
(
NAME varchar2(10),
ID number PRIMARY KEY
)
Table A:
CREATE TABLE a_table
(
CODE number,
RATE number,
DATEE date,
CONSTRAINT fk_a_table
FOREIGN KEY (CODE) REFERENCES b_table(ID)
);
Table C:
CREATE TABLE c_table
(
DATEE date,
USD number,
EUR number,
OBP number
);

You can use conditional aggregation:
insert into c_table (DATEE, USD, EUR, OBP)
select a.DATEE,
max(case WHEN a.code = 1 THEN a.rate end),
max(case WHEN a.code = 2 THEN a.rate end),
max(case WHEN a.code = 3 THEN a.rate end)
from a_table a
group by a.DATEE

begin
for r in(
with m as(
select 1 as xcode, 1000 as xrate, sysdate as xdate from dual
union all
select 2, 2000, sysdate from dual
union all
select 3, 3000, sysdate-1 from dual
union all
select 1, 2000, sysdate from dual
union all
select 2, 4000, sysdate from dual
union all
select 3, 5000, sysdate-2 from dual
)
select * from m
pivot(
sum(xrate) for xcode in (
1 as usd,
2 as eur,
3 as gbp
))
)loop
update your_table set foo = r.xrate, foo_currency_usd = x.usd where foo_date = r.date;
end loop;
end;

Try the below code.
INSERT INTO c_table(DATEE, USD, EUR, OBP)
Select DATEE, USD, EUR, OBP from(
Select
TO_CHAR(a.DATEE,'DD-MON-YYYY') DATEE, a.Rate, b.name from a_table a
inner join b_table b on b.id=a.code
)PIVOT(SUM(rate)for name in ('USD' USD, 'EUR' EUR,'OBP' OBP));

Related

How to compare different values within the same column

I having two tables emp and type.
create table EMP(ID number(10), effective_date date);
EID Effective_date
--------------------
1 02/14/2023
2 02/15/2023
3 04/30/2023
4 03/24/2023
create table type(ID number(10),contract_type varchar2(2));
TID contract_type
------------------
1 P
1 S
1 P
2 S
2 S
3 P
3 S
4 S
I am looking EID which is having contract type is 'S' in type table. (or emp table with effective date is greater than sysdate and in the type table with only contract_type ='S')
Actual result :
2
4
My query is not giving the correct results.
select emp.EID
from emp,type
where EID = TID
contract_type ='S'
effective_date >= sysdate
group by TID
having count(TID) >= 1;
If you want to keep your idea with COUNT and GROUP BY, you should count other contract types than the 'S' ones and check this is 0:
SELECT e.eid
FROM emp e
JOIN type t ON e.eid = t.tid
WHERE
e.effective_date >= sysdate
GROUP BY e.eid
HAVING COUNT(CASE WHEN t.contract_type <> 'S' THEN 1 END) = 0;
This query will return 2 and 4 for your sample data.
Try out: db<>fiddle
Another option is as already said here using NOT EXISTS.
Take care of following difference to the NOT EXISTS approach: The query in Tim's answer will also fetch id's of table "emp" that don't appear at all in table "type". My query here will not fetch such id's.
It's up to you to decide whether this is possible at all and what to do in this case.
Changing JOIN to LEFT JOIN in above query will eliminate this difference.
I would use exists logic here:
SELECT EID
FROM EMP e
WHERE effective_date >= SYSDATE AND
NOT EXISTS (
SELECT 1
FROM "type" t
WHERE t.TID = e.EID AND
t.contract_type <> 'S'
);
You could use Count() Over() analytic function to check for type 'S' and number of different types per ID.
SELECT DISTINCT ID
FROM ( Select e.EID "ID",
Count(CASE t.CONTRACT_TYPE WHEN 'S' THEN 'S' END) Over(Partition By t.ID Order By t.ID) "NUM_OF_S",
Count(Distinct t.CONTRACT_TYPE) Over(Partition By t.ID) "NUM_OF_TYPES",
TRUNC(e.EFFECTIVE_DATE) - TRUNC(SYSDATE) "DAYS_AFTER_SYSDATE"
From emp_cte e
Inner Join type_cte t ON(t.ID = e.EID) )
WHERE NUM_OF_S > 0 And -- Type 'S' exists for ID AND
NUM_OF_TYPES = 1 And -- It is the only type AND
DAYS_AFTER_SYSDATE > 0 -- EFFECTIVE_DATE is after SYSDATE
With your sample data ...
WITH
emp_cte(EID, EFFECTIVE_DATE) AS
(
Select 1, To_Date('02/14/2023', 'mm/dd/yyyy') From Dual Union All
Select 2, To_Date('02/15/2023', 'mm/dd/yyyy') From Dual Union All
Select 3, To_Date('04/30/2023', 'mm/dd/yyyy') From Dual Union All
Select 4, To_Date('03/24/2023', 'mm/dd/yyyy') From Dual
),
type_cte(ID, CONTRACT_TYPE) AS
(
Select 1, 'P' From Dual Union All
Select 1, 'S' From Dual Union All
Select 1, 'P' From Dual Union All
Select 2, 'S' From Dual Union All
Select 2, 'S' From Dual Union All
Select 3, 'P' From Dual Union All
Select 3, 'S' From Dual Union All
Select 4, 'S' From Dual
)
... result would be ...
-- ID
-- ----------
-- 2
-- 4

How to compare column in one table with array from another table in BigQuery?

Just continue from the answer for my previous question.
I want to get all values from table b (in rows) if there is any difference between values in arrays from table a by same ids
WITH a as (SELECT 1 as id, ['123', 'abc', '456', 'qaz', 'uqw'] as value
UNION ALL SELECT 2, ['123', 'wer', 'thg', '10', '200']
UNION ALL SELECT 3, ['200']
UNION ALL SELECT 4, null
UNION ALL SELECT 5, ['140']),
b as (SELECT 1 as id, '123' as value
UNION ALL SELECT 1, 'abc'
UNION ALL SELECT 1, '456'
UNION ALL SELECT 1, 'qaz'
UNION ALL SELECT 1, 'uqw'
UNION ALL SELECT 2, '123'
UNION ALL SELECT 2, 'wer'
UNION ALL SELECT 2, '10'
UNION ALL SELECT 3, null
UNION ALL SELECT 4, 'wer'
UNION ALL SELECT 4, '234'
UNION ALL SELECT 5, '140'
UNION ALL SELECT 5, '121'
)
SELECT * EXCEPT(flag)
FROM (
SELECT b.*, COUNTIF(b.value IS NULL) OVER(PARTITION BY id) flag
FROM a LEFT JOIN a.value
FULL OUTER JOIN b
USING(id, value)
)
WHERE flag > 0
AND NOT id IS NULL
It works well for all ids except 5.
In my case I need to return all values if there is any difference.
In example array with id 5 from table a has only one value is '140' while there are two rows with values by id 5 from table b. So in this case all values by id 5 from table b also must appear in expected output
How need to modify this query to get what I want?
UPDATED
Seems like it works for me. But I can not be sure for 100%
SELECT * EXCEPT(flag)
FROM (
SELECT b.*, COUNTIF((b.value IS NULL AND a.value IS NOT NULL) OR (b.value IS NOT NULL AND a.value IS NULL)) OVER(PARTITION BY id) flag
FROM a LEFT JOIN a.value
FULL OUTER JOIN b
USING(id, value)
)
WHERE flag > 0
AND NOT id IS NULL
#standardSQL
SELECT *
FROM table_b
WHERE id IN (
SELECT id FROM table_a a
JOIN table_b b USING(id)
GROUP BY id
HAVING STRING_AGG(IFNULL(b.value, 'NULL') ORDER BY b.value) !=
IFNULL(ANY_VALUE((SELECT STRING_AGG(IFNULL(value, 'NULL') ORDER BY value) FROM a.value)), 'NULL')
)

How to group by and select

I tried to extract customer who has type a
I guess I must group by in customer and tried to having in type
customer type
A a
A c
B b
B c
C a
C a
but I couldn't figure out specific way to achieve this.
If someone has opinion,please let me know.
My desired result is following
customer type
A a
A c
C a
C a
Thanks
Using exists, we can try:
SELECT t1.customer, t1.type
FROM yourTable t1
WHERE EXISTS (SELECT 1 FROM yourTable t2 WHERE t2.customer = t1.customer AND t2.type = 'a');
The exists logic reads in plain English as select any record for which we can find at least one record for the same customer whose type is a. This means retain all customer records, where at least one of those records has type a.
You don't need to group. You can just filter the table for customers that have type 'a' and use that resultset to filter the table again for the customers.
WITH
cust_data
AS
(SELECT 'A' AS customer, 'a' AS TYPE FROM DUAL
UNION ALL
SELECT 'A' AS customer, 'c' AS TYPE FROM DUAL
UNION ALL
SELECT 'B' AS customer, 'b' AS TYPE FROM DUAL
UNION ALL
SELECT 'B' AS customer, 'c' AS TYPE FROM DUAL
UNION ALL
SELECT 'C' AS customer, 'a' AS TYPE FROM DUAL
UNION ALL
SELECT 'C' AS customer, 'a' AS TYPE FROM DUAL)
SELECT *
FROM cust_data c
WHERE customer IN (SELECT customer
FROM cust_data
WHERE TYPE = 'a');
This gives
CUSTOMER TYPE
A c
A a
C a
C a
I hope the below query completes your requirement.
SELECT
*
FROM
test.customer
WHERE
customer IN (SELECT
customer
FROM
test.customer
WHERE
type = 'a');
Output:
A a
A c
C a
C a
Do not use accessing table twice. Use window functions instead.
with t(customer, type) as (
select 'A', 'a' from dual union all
select 'A', 'c' from dual union all
select 'B', 'b' from dual union all
select 'B', 'c' from dual union all
select 'C', 'a' from dual union all
select 'C', 'a' from dual)
select customer, type
from
(select t.*, count(decode(type, 'a', 1)) over (partition by customer) cnt
from t
)
where cnt > 0;
CUSTOMER TYPE
-------- ----
A a
A c
C a
C a

SQL - split values into buckets using reference table

I've tried to describe the challenge below; but perhaps the best way to understand might be to run the sample SQL and work backwards from the sample output table (#IncomingSplitBucket)
Im certain there is an eloquent way to code this – but its beyond any of my best efforts.
The challenge is titled:
Splitting Numbers into Buckets
Each Customer has Payment Buckets (#bucket). I’m looking to Assign payments into Buckets as the payments are made see table (#incoming).
Payments can span buckets and can be for +/- amounts.
Using the (#incoming) and (#buckets) information – the AIM is to assign the payments into buckets. Payments should be split when they span a bucket amount.
The table #IncomingSplitBucket – provide the desired OUTPUT. One way to understand the requirements is to perhaps look at this and work backwards.
I have tried and failed many approaches to this problem.
/*
Please run code and review
*/
--===========================================
--t1 - PAYMENT SCHEDULE SPLIT INTO BUCKET
--===========================================
DECLARE #bucket TABLE (
CustID INT,
BucketSeqID char(1),
Amount money
)
INSERT INTO #bucket
SELECT 1,'a', '1000' union
SELECT 1,'b', '1000' union
SELECT 1,'c', '2000' union
SELECT 1,'d', '2000' union
SELECT 2,'a', '5000'union
SELECT 2,'b', '5000'union
SELECT 2,'c', '1000'union
SELECT 2,'d', '1000'union
SELECT 3,'a', '5000' union
SELECT 3,'b', '5000'
--===========================================
--t2 - PAYMENTS COMING IN
--===========================================
DECLARE #incoming TABLE (
CustID INT,
IncomingSeqID INT,
Amount money
)
INSERT INTO #incoming
SELECT 1,1, '1000' union
SELECT 1,2, '2000' union
SELECT 1,3, '3000' union
SELECT 2,1, '5000' union
SELECT 2,2, '3000' union
SELECT 2,3, '2000' union
SELECT 2,4, '2000' union
SELECT 3,1, '3000' union
SELECT 3,2, '3000' union
SELECT 3,3, '3000' union
SELECT 3,4, '1000'
--=================================================================
--t3 - THIS IS WHAT THE OUTPUT DATA SHOULD LOOK LIKE
--================================================================
DECLARE #IncomingSplitBucket TABLE (
CustID INT,
IncomingSeqID INT,
BucketSeqID char(1),
AmountBucket money
)
INSERT INTO #IncomingSplitBucket
SELECT 1,1,'a','1000' union
SELECT 1,2,'b','1000' union
SELECT 1,2,'c','1000' union
SELECT 1,3,'c','1000' union
SELECT 1,3,'d','2000' union
SELECT 2,1,'a','5000' union
SELECT 2,2,'b','3000' union
SELECT 2,3,'b','2000' union
SELECT 2,4,'c','1000' union
SELECT 2,4,'d','1000' union
SELECT 3,1,'a','3000' union
SELECT 3,2,'a','2000' union
SELECT 3,2,'b','1000' union
SELECT 3,3,'b','3000' union
SELECT 3,4,'b','1000'
--=================================================================
--Outputs and Data Checks
--================================================================
--REVIEW DATA
select * from #bucket
select * from #incoming
select * from #IncomingSplitBucket --(sample output)
--DATA Check - The SUM AmountBucket of Grouped BucketSeqID = the #bucket amounts see table
SELECT CustID, BucketSeqID, SUM(AmountBucket) AS BucketCheck
FROM #IncomingSplitBucket
GROUP BY CustID, BucketSeqID
order by 1,2
--DATA Check - The SUM AmountBucket of Grouped IncomingSeqID = the #incoming amounts see table
SELECT CustID, IncomingSeqID, SUM(AmountBucket) AS BucketCheck
FROM #IncomingSplitBucket
GROUP BY CustID, IncomingSeqID
order by 1,2
Updated complexity request: (10/12/2019)
When negative amounts are received that take money out of the
buckets.
When the amount received is greater than buckets – an ‘overflow
bucket’ is used (called ‘x’ in the Expect Output)
Thanks
--===========================================
--t1 - BUCKETS
--===========================================
DECLARE #bucket TABLE (
CustID INT,
BucketSeqID char(1),
Amount money
)
INSERT INTO #bucket
SELECT 1,'a', '1000' union
SELECT 1,'b', '1000' union
SELECT 1,'c', '2000' union
SELECT 1,'d', '2000' union
SELECT 2,'a', '5000'union
SELECT 2,'b', '5000'union
SELECT 2,'c', '1000'union
SELECT 2,'d', '1000'union
SELECT 3,'a', '5000' union
SELECT 3,'b', '5000'
--===========================================
--t2 - PAYMENTS
--===========================================
DECLARE #incoming TABLE (
CustID INT,
IncomingSeqID INT,
Amount money
)
INSERT INTO #incoming
SELECT 1,1, '1000' union
SELECT 1,2, '2000' union
SELECT 1,3, '3000' union
SELECT 2,1, '5000' union
SELECT 2,2, '3000' union
SELECT 2,3, '2000' union
SELECT 2,4, '2000' union
SELECT 2,5, '-3000' union
SELECT 3,1, '3000' union
SELECT 3,2, '3000' union
SELECT 3,3, '3000' union
SELECT 3,4, '500' union
SELECT 3,5, '200' union
SELECT 3,6, '-500' union
SELECT 3,7, '800' union
SELECT 3,8, '-400' union
SELECT 3,9, '500'
--=================================================================
--t3 - EXPECTED OUTPUT
--================================================================
DECLARE #IncomingSplitBucket TABLE (
CustID INT,
IncomingSeqID INT,
BucketSeqID char(1),
AmountBucket money
)
INSERT INTO #IncomingSplitBucket
SELECT 1,1,'a','1000' union
SELECT 1,2,'b','1000' union
SELECT 1,2,'c','1000' union
SELECT 1,3,'c','1000' union
SELECT 1,3,'d','2000' union
SELECT 2,1,'a','5000' union
SELECT 2,2,'b','3000' union
SELECT 2,3,'b','2000' union
SELECT 2,4,'c','1000' union
SELECT 2,4,'d','1000' union
SELECT 2,5,'d','-1000' union
SELECT 2,5,'c','-1000' union
SELECT 2,5,'b','-1000' union
SELECT 3,1,'a','3000' union
SELECT 3,2,'a','2000' union
SELECT 3,2,'b','1000' union
SELECT 3,3,'b','3000' union
SELECT 3,4,'b','200' union
SELECT 3,5,'b','-500' union
SELECT 3,6,'b','800' union
SELECT 3,7,'b','-400' union
SELECT 3,8,'b','400' union
SELECT 3,8,'x','100'
--=================================================================
--Outputs and Data Checks
--================================================================
--REVIEW DATA
select * from #bucket
select * from #incoming
select * from #IncomingSplitBucket --(expected output)
--DATA Check - The SUM AmountBucket of Grouped BucketSeqID = the #bucket amounts see table
SELECT CustID, BucketSeqID, SUM(AmountBucket) AS BucketCheck
FROM #IncomingSplitBucket
GROUP BY CustID, BucketSeqID
order by 1,2
--DATA Check - The SUM AmountBucket of Grouped IncomingSeqID = the #incoming amounts see table
SELECT CustID, IncomingSeqID, SUM(AmountBucket) AS BucketCheck
FROM #IncomingSplitBucket
GROUP BY CustID, IncomingSeqID
order by 1,2
First I will use a common table expression (cte) to change the column names so I dont have similar ones between tables to make our life easy, plus will convert the bucket names a,b,c,d to a seq of 1,2,3,4 for simplicity.
Then I will continue with another recursive cte to take the first bucket and the first incoming payment, if the bucket is not filled for the next record I will use the same unfilled bucket else i will use the next bucket, same thing with the incoming payment if the incoming payment fits in the remaining part of the bucket on next row i will go to the next payment if not I will use the rest of the incoming payment till all incoming payment are finished.
Please see the below CTE
;with bucket as (
select CustID BucketCustID,BucketSeqID,
case BucketSeqID when 'a' then 1 when 'b' then 2 when 'c' then 3 when 'd' then 4 end BucketSeq
,Amount bucketAmount from #bucket
),incoming as (
select CustID IncomingCustID, IncomingSeqID ,Amount [IncomingAmount] from #incoming
),result as (
select BucketCustID,IncomingSeqID,BucketSeqID,BucketSeq
,case when bucketAmount<IncomingAmount then 0 else bucketAmount-IncomingAmount end bucketAmount
,case when bucketAmount>IncomingAmount then 0 else IncomingAmount-bucketAmount end IncomingAmount
,case when bucketAmount>IncomingAmount then IncomingAmount else bucketAmount end InBucket
from bucket b
inner join incoming i on i.IncomingCustID=b.BucketCustID and i.IncomingSeqID=1
where b.BucketSeq=1
union all
select BucketCustID,IncomingSeqID,BucketSeqID,BucketSeq
,case when bucketAmount<IncomingAmount then 0 else bucketAmount-IncomingAmount end bucketAmount
,case when bucketAmount>IncomingAmount then 0 else IncomingAmount-bucketAmount end IncomingAmount
,case when bucketAmount>IncomingAmount then IncomingAmount else bucketAmount end InBucket
from (
select
b.BucketCustID,i.IncomingSeqID,b.BucketSeqID,b.BucketSeq
,case when r.BucketSeq=b.BucketSeq then r.bucketAmount else b.bucketAmount end bucketAmount
,case when r.IncomingSeqID=i.IncomingSeqID then r.IncomingAmount else i.IncomingAmount end IncomingAmount
from result r
inner join bucket b on b.BucketCustID=r.BucketCustID and b.BucketSeq=r.BucketSeq+(case when r.bucketAmount=0 then 1 else 0 end)
inner join incoming i on i.IncomingCustID=r.BucketCustID and i.IncomingSeqID=r.IncomingSeqID+(case when r.IncomingAmount=0 then 1 else 0 end)
) Prev
)
select BucketCustID CustID,IncomingSeqID,BucketSeqID,InBucket AmountBucket
from result r
order by BucketCustID,IncomingSeqID,BucketSeqID
The output matches your desired output as below:-
CustID IncomingSeqID BucketSeqID AmountBucket
1 1 a 1000.00
1 2 b 1000.00
1 2 c 1000.00
1 3 c 1000.00
1 3 d 2000.00
2 1 a 5000.00
2 2 b 3000.00
2 3 b 2000.00
2 4 c 1000.00
2 4 d 1000.00
3 1 a 3000.00
3 2 a 2000.00
3 2 b 1000.00
3 3 b 3000.00
3 4 b 1000.00

SQL: case when statement with over (partition by)

I'm quite new here and have tried various hints from Stackoverflow posts on SQL but haven't been able to solve this one.
I have a table that is result of tables joined, looks like this
Table A
cust_id prod_type
001 A
001 A
002 A
002 B
003 A
003 C
I need to apply logic: If for each cust_id there is at least one value where prod_type is B or C, then return corresponding prod_type value. If for each cust_id all values of prod_type are A, return A.
The final output i am trying to get is
Table B
cust_id prod_type
001 A
002 B
003 C
I have tried using
SELECT
A.cust_id
,CASE WHEN prod_type in ('B', 'C') THEN prod_type OVER (PARTITION BY A.cust_id)
ELSE 'A' OVER (PARTITION BY A.cust_id) END AS product
FROM ([Joined Tables]) AS A
and it seems that teradata does not allow to use over(clause) in a case statement: expects 'END' keyword between prod_type and OVER keyword.
You want to return only one row per customer with the best matching product_type?
If there are additional columns:
SELECT
A.cust_id
,prod_type
,...
FROM ([Joined Tables]) AS A
QUALIFY
ROW_NUMBER()
OVER (PARTITION BY CUST_ID
ORDER BY CASE WHEN prod_type in ('B', 'C') -- best match first
THEN 1
ELSE 2
END,
prod_type) = 1
Otherwise #Frisbee's MAX will work, but I assume that A/B/C are not your actual product names:
SELECT
A.cust_id
,COALESCE(MAX(CASE WHEN prod_type in ('B', 'C') THEN prod_type END)
,MAX(CASE WHEN prod_type not in ('B', 'C') THEN prod_type END))
FROM ([Joined Tables]) AS A
GROUP BY just_id
For completness here is an equivalent using the function first_value and using the inverse alphabetical order of your products.
Similarly to #Amgalan Bilegjav answer, 'b' is the sample table and 'a' the table with an extra column (finding the first product here).
The code was tested with Teradata version 16.20.53.55
SELECT
a.CUST_ID
, a.PROD_TYPE
FROM (
SELECT
b.CUST_ID
, first_value(b.PROD_TYPE) over (partition by b.CUST_ID order by b.PROD_TYPE desc) as PROD_TYPE
FROM (
/* recreate the exemple table */
select * from (SELECT '001' CUST_ID, 'A' PROD_TYPE) as "DUAL" UNION all
select * from (SELECT '001' CUST_ID, 'A' PROD_TYPE) as "DUAL" UNION all
select * from (SELECT '002' CUST_ID, 'A' PROD_TYPE) as "DUAL" UNION all
select * from (SELECT '002' CUST_ID, 'B' PROD_TYPE) as "DUAL" UNION all
select * from (SELECT '003' CUST_ID, 'A' PROD_TYPE) as "DUAL" UNION all
select * from (SELECT '003' CUST_ID, 'C' PROD_TYPE) as "DUAL"
) b
) a
group by a.CUST_ID, a.PROD_TYPE
order by a.CUST_ID
;
This query recreates your sample data (table b.) Add the row numbers based on specific product order (ranging from C, B to A in the table a) then filter on the first row to get the wanted table.
SELECT
a.CUST_ID
, a.PROD_TYPE
FROM (
SELECT
b.CUST_ID
, b.PROD_TYPE
, ROW_NUMBER() OVER (PARTITION BY CUST_ID ORDER BY DECODE(PROD_TYPE, 'A',3,'B',2,'C',1)) as RN
FROM (
/* recreate the exemple table */
select * from (SELECT '001' CUST_ID, 'A' PROD_TYPE) as "DUAL" UNION all
select * from (SELECT '001' CUST_ID, 'A' PROD_TYPE) as "DUAL" UNION all
select * from (SELECT '002' CUST_ID, 'A' PROD_TYPE) as "DUAL" UNION all
select * from (SELECT '002' CUST_ID, 'B' PROD_TYPE) as "DUAL" UNION all
select * from (SELECT '003' CUST_ID, 'A' PROD_TYPE) as "DUAL" UNION all
select * from (SELECT '003' CUST_ID, 'C' PROD_TYPE) as "DUAL"
) b
) a
WHERE RN = 1
order by a.CUST_ID
;