From multiple tables I'm currently selecting a product, value, and contract period. I want to group the results by product and shipment period while summing the value.
My contract period can either be arrival based or shipment based. So, currently to determine which contract period to use, I'm looking to see if one of the period descriptions is null then populating the period end and begin dates as either ship or arrival based on that. Specifically, I'm using the following.
DECODE((P.SHIP_PERIOD_DESCR), NULL,
'ARRIVE' || ' ' ||P.ARRIVAL_PERIOD_BEGIN || ' - ' || P.ARRIVAL_PERIOD_END,
'SHIP' || ' ' ||P.SHIP_PERIOD_BEGIN || ' - ' || P.SHIP_PERIOD_END)
My results are as such:
PRODUCT VALUE CONTRACT_PERIOD
APPLES $600 SHIP 01-FEB-16 - 15-MAR-16
APPLES $700 SHIP 01-MAR-16 - 15-APR-16
LEMONS $200 SHIP 15-JAN-16 - 31-JAN-16
LEMONS $150 SHIP 01-FEB-16 - 15-FEB-16
LEMONS $200 ARRIVE 15-FEB-16 - 28-FEB-16
LEMONS $250 ARRIVE 01-MAR-16 - 15-MAR-16
What I would like to see is the min ship or arrival date and max ship or arrival date per product as such:
PRODUCT VALUE CONTRACT_PERIOD
APPLES $1,300 SHIP 01-FEB-16 - 15-APR-16
LEMONS $350 SHIP 15-JAN-16 - 15-FEB-16
LEMONS $450 ARRIVE 15-FEB-16 - 15-MAR-16
Any suggestions on a way to determine which contract is valid, then group the results using the min and max dates while not interchanging a ship date for an arrival date would be greatly appreciated.
Oracle Setup:
CREATE TABLE table_name ( product, value, ship_period_descr, arrival_period_begin, arrival_period_end, ship_period_begin, ship_period_end ) AS
SELECT 'Apples', 600, 'X', NULL, NULL, DATE '2016-02-01', DATE '2016-03-15' FROM DUAL UNION ALL
SELECT 'Apples', 700, 'X', NULL, NULL, DATE '2016-03-01', DATE '2016-04-16' FROM DUAL UNION ALL
SELECT 'Lemons', 200, 'X', NULL, NULL, DATE '2016-01-15', DATE '2016-01-31' FROM DUAL UNION ALL
SELECT 'Lemons', 150, 'X', NULL, NULL, DATE '2016-02-01', DATE '2016-02-15' FROM DUAL UNION ALL
SELECT 'Lemons', 200, NULL, DATE '2016-02-15', DATE '2016-02-28', NULL, NULL FROM DUAL UNION ALL
SELECT 'Lemons', 250, NULL, DATE '2016-03-01', DATE '2016-03-15', NULL, NULL FROM DUAL;
Query:
SELECT Product,
SUM( Value ) AS Value,
DECODE(
DECODE( P.SHIP_PERIOD_DESCR, NULL, 1, 0 ),
1, 'ARRIVE ' || MIN( P.ARRIVAL_PERIOD_BEGIN ) || ' - ' || MAX( P.ARRIVAL_PERIOD_END ),
'SHIP ' || MIN( P.SHIP_PERIOD_BEGIN ) || ' - ' || MAX( P.SHIP_PERIOD_END )
) AS Contract_Period
FROM table_name p
GROUP BY Product,
DECODE( P.SHIP_PERIOD_DESCR, NULL, 1, 0 );
Results:
PRODUCT VALUE CONTRACT_PERIOD
------- ---------- ----------------------------------------------
Apples 1300 SHIP 01-FEB-16 - 16-APR-16
Lemons 350 SHIP 15-JAN-16 - 15-FEB-16
Lemons 450 ARRIVE 15-FEB-16 - 15-MAR-16
The basic idea is not to combine the different columns into a single concatenated column. Then use intelligent aggregation:
with t as (
<basically your query here, but with each column individually>
)
select product, ship_period_desc,
min(case when ship_period_desc = 'ARRIVAL' then ARRIVAL_PERIOD_BEGIN
else SHIP_PERIOD_BEGIN
end) as PERIOD_BEGIN,
min(case when ship_period_desc = 'ARRIVAL' then ARRIVAL_PERIOD_END
else SHIP_PERIOD_END
end) as PERIOD_END
from t
where ship_period_desc in ('ARRIVAL', 'SHIP')
group by product, ship_period_desc;
Related
I have two tables
Payments_history
CREATE TABLE payments_history(payment_date , account_id , currency, amount) AS
SELECT TO_DATE ('05/01/2022','DD/MM/YYYY'), 2291969088, 'GBP', 10.00 FROM DUAL UNION ALL
SELECT TO_DATE ('05/01/2022','DD/MM/YYYY'), 7851880663, 'USD', 20 FROM DUAL UNION ALL
SELECT TO_DATE ('06/01/2022','DD/MM/YYYY'), 5326844767, 'USD', 3.000 FROM DUAL UNION ALL
SELECT TO_DATE ('05/01/2022','DD/MM/YYYY'), 3668657617, 'EUR', 40 FROM DUAL UNION ALL
SELECT TO_DATE ('06/01/2022','DD/MM/YYYY'), 9040142052, 'GBP', 30.000 FROM DUAL
Historics_rates
Create TABLE Historics_rates(t_date,from_ccy,to_ccy,rate) AS
SELECT TO_DATE ('06/01/2022','DD/MM/YYYY'),'GBP','EUR',1.1832 FROM DUAL UNION ALL
SELECT TO_DATE ('06/01/2022','DD/MM/YYYY'),'AUD','GBP',0.5263 FROM DUAL UNION ALL
SELECT TO_DATE ('06/01/2022','DD/MM/YYYY'),'EUR','GBP',0.8452 FROM DUAL UNION ALL
SELECT TO_DATE ('05/01/2022','DD/MM/YYYY'),'USD','GBP',0.7388 FROM DUAL UNION ALL
SELECT TO_DATE ('05/01/2022','DD/MM/YYYY'),'EUR','USD',1.1441 FROM DUAL
what I am trying to find is the 'daily amount GBP Equivalent per day for the last three months'. For example on date '05/01/2022 ' check the given amount in Payments_history table if the currency is GBP add it into total and move on to the next payment, next if the currency is in USD check for its equivalent in GBP rate and convert it into GBP and print the result. If the amount is in some currency whose from_ccy is not GBP skip that transaction
Date Amount in GBP
2022-01-05 24.78
This is what I have done so far
DECLARE
total NUMBER;
BEGIN
select PH.PAYMENT_DATE,
CASE
WHEN PH.CURRENCY = 'GBP' THEN total = total + PH.AMOUNT
WHEN PH.CURRENCY = 'USD' AND HR.from_ccy = 'USD' AND HR.to_ccy = 'GBP' THEN total = total + (PH.AMOUNT*HR."rate")
WHEN PH.CURRENCY = 'AUD' AND HR.from_ccy = 'AUD' AND HR.to_ccy = 'GBP' THEN total = total + (PH.AMOUNT*HR."rate")
WHEN PH.CURRENCY = 'EUR' AND HR.from_ccy = 'EUR' AND HR.to_ccy = 'GBP' THEN total = total + (PH.AMOUNT*HR."rate")
ELSE 'CURRENCY NOT FOUND'
END AS total
FROM "historic_rates" AS HR RIGHT JOIN PAYMENTS_HISTORY AS PH on HR."date" = PH.payment_date AND PH.Currency = HR."from_ccy"
WHERE Extract(Month from PH.payment_date) = Extract(month from add_months( sysdate, -3 )) GROUP BY PH.PAYMENT_DATE;
but it's giving me error
[Err] ORA-06550: line 8, column 40:
PL/SQL: ORA-00905: missing keyword
ORA-06550: line 6, column 1:
PL/SQL: SQL Statement ignored
ORA-06550: line 15, column 117:
PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following:
( begin case declare end exception exit for goto if loop mod
null pragma raise return select update while with
<an identifier> <a double-quot
Your errors include:
Using = inside the THEN clause of a CASE expression is invalid syntax.
You cannot refer to a column alias inside the sub-query where it is defined (except in an ORDER BY clause) so you cannot use total inside the CASE expression.
A CASE expression needs to have the same data type in all of its outputs so you cannot mix numbers and strings.
You GROUP BY PH.PAYMENT_DATE but you do not have any aggregation function around the CASE expression.
If you do aggregate then 'CURRENCY NOT FOUND' is not something you can total.
In Oracle, AS before a table alias is invalid syntax.
You have a mix of quoted identifiers HR."from_ccy" and unquoted identifiers HR.from_ccy. While it is possible to have both in a table, one of them is almost certainly wrong (and using quoted identifiers is bad practice) and your DDL statements do not use quotes.
Unless you really do intend to get rows from December of any year, do not compare only months. Compare on a range from the start of the month 3-months ago until before the start of the month two-months ago.
Your PL/SQL block does not have an END statement.
You are using a SELECT statement in PL/SQL without using SELECT ... INTO.
You probably don't want to use PL/SQL.
None of your sample data is from 3 months ago (December 2021).
Something like this:
select PH.PAYMENT_DATE,
SUM(
CASE
WHEN PH.CURRENCY = 'GBP'
THEN PH.AMOUNT
WHEN PH.CURRENCY IN ('USD', 'AUD', 'EUR')
AND HR.from_ccy = PH.CURRENCY
AND HR.to_ccy = 'GBP'
THEN PH.AMOUNT*HR.rate
END
) As total
FROM historics_rates HR
RIGHT JOIN PAYMENTS_HISTORY PH
on HR.t_date = PH.payment_date
AND PH.Currency = HR.from_ccy
WHERE PH.payment_date >= ADD_MONTHS(TRUNC(SYSDATE, 'MM'), -3)
AND PH.payment_date < ADD_MONTHS(TRUNC(SYSDATE, 'MM'), -2)
GROUP BY PH.PAYMENT_DATE;
Which, for the sample data:
CREATE TABLE payments_history(payment_date , account_id , currency, amount) AS
SELECT TO_DATE ('05/12/2021','DD/MM/YYYY'), 2291969088, 'GBP', 10.00 FROM DUAL UNION ALL
SELECT TO_DATE ('05/12/2021','DD/MM/YYYY'), 7851880663, 'USD', 20 FROM DUAL UNION ALL
SELECT TO_DATE ('06/12/2021','DD/MM/YYYY'), 5326844767, 'USD', 3.000 FROM DUAL UNION ALL
SELECT TO_DATE ('05/12/2021','DD/MM/YYYY'), 3668657617, 'EUR', 40 FROM DUAL UNION ALL
SELECT TO_DATE ('06/12/2021','DD/MM/YYYY'), 9040142052, 'GBP', 30.000 FROM DUAL;
Create TABLE Historics_rates(t_date,from_ccy,to_ccy,rate) AS
SELECT TO_DATE ('06/12/2021','DD/MM/YYYY'),'GBP','EUR',1.1832 FROM DUAL UNION ALL
SELECT TO_DATE ('06/12/2021','DD/MM/YYYY'),'AUD','GBP',0.5263 FROM DUAL UNION ALL
SELECT TO_DATE ('06/12/2021','DD/MM/YYYY'),'EUR','GBP',0.8452 FROM DUAL UNION ALL
SELECT TO_DATE ('05/12/2021','DD/MM/YYYY'),'USD','GBP',0.7388 FROM DUAL UNION ALL
SELECT TO_DATE ('05/12/2021','DD/MM/YYYY'),'EUR','USD',1.1441 FROM DUAL;
Outputs:
PAYMENT_DATE
TOTAL
06-DEC-21
30
05-DEC-21
24.776
db<>fiddle here
I have table which has around 5000 records with 4 types of ordes at multiple timestamps, i have been tryint to calculate the count of orders at multiple timestamps like 00 to 04hrs, 04hrs to 24hrs. All i can acheive the result for either of 1 timestamp.
appriciate your help :)
SELECT
DECODE(order_type,1,'Multi Sim',2,'Single Sim',3,'Sim Replacement',7,'Data SIM with Router') AS order_type,
COUNT(order_type) AS total0hrs_4hrs,
COUNT(order_type) AS total4hrs_24hrs,
COUNT(order_type) AS grand_total
FROM
sids
WHERE
order_time >= to_timestamp('2020-09-01 00:00:00.0000','YYYY-MM-DD HH24:MI:SS.FF4')
AND order_time <= to_timestamp('2020-09-01 03:59:59.9999','YYYY-MM-DD HH24:MI:SS.FF4')
AND order_type IN (
1,
2,
3,
7
)
GROUP BY
order_type
HAVING
COUNT(order_type) > 0
ORDER BY
1;
Use a CASE expression within your aggregation functions:
SELECT DECODE(
order_type,
1, 'Multi Sim',
2, 'Single Sim',
3, 'Sim Replacement',
7, 'Data SIM with Router'
) AS order_type,
COUNT(
CASE
WHEN order_time - TRUNC( order_time ) < INTERVAL '4' HOUR
THEN 1
ELSE NULL
END
) AS total0hrs_4hrs,
COUNT(
CASE
WHEN order_time - TRUNC( order_time ) < INTERVAL '4' HOUR
THEN NULL
ELSE 1
END
) AS total4hrs_24hrs,
COUNT(order_type) AS grand_total
FROM sids
WHERE order_time >= TIMESTAMP '2020-09-01 00:00:00.0000'
AND order_time < TIMESTAMP '2020-09-02 00:00:00.0000'
AND order_type IN ( 1, 2, 3, 7 )
GROUP BY
order_type
ORDER BY
1;
I am writing a query that calculates whether the correct booking fee was charged for all bookings. The structure of the tables are below. Please note that due to the environment I am working in I cannot create any stored procedures or functions to help me.
BOOKINGS
Date date,
CustomerId int,
BookingTypeId int,
FeeCharged money
FEES
BookingTypeId int,
FeeAmount money,
EffectiveFrom date
Given the example data set below how do I query the data to ensure the fee charged per booking matches the correct fee in the fees table based on the effective from date:
try this
WITH
Fees (BookingTypeId, FeeAmount, EffectiveFrom) AS (
SELECT 1, 50, '2019-06-01'
UNION ALL SELECT 1, 55, '2019-09-01'
),
Bookings (Date, CustomerId, BookingTypeId, FeeCharged) AS (
SELECT '2019-07-01', 1, 1, 50
UNION ALL SELECT '2019-07-02', 1, 1, 50
UNION ALL SELECT '2019-10-01', 1, 1, 150
),
BookingFeeUpdate as (
SELECT
Bookings.BookingTypeId,
Bookings.Date,
MAX(EffectiveFrom) LastFeeUpdate
FROM
Bookings
LEFT JOIN Fees ON
Fees.BookingTypeId = Bookings.BookingTypeId
AND Fees.EffectiveFrom <= Bookings.Date
GROUP BY
Bookings.BookingTypeId,
Bookings.Date
)
SELECT
Bookings.*, Fees.FeeAmount as CorrectFee, Fees.EffectiveFrom, case Fees.FeeAmount when FeeCharged then 1 else 0 end Correct
FROM
Bookings
INNER JOIN BookingFeeUpdate ON
BookingFeeUpdate.BookingTypeId = Bookings.BookingTypeId
AND BookingFeeUpdate.Date = Bookings.Date
LEFT JOIN Fees ON
Fees.BookingTypeId = Bookings.BookingTypeId
AND Fees.EffectiveFrom = BookingFeeUpdate.LastFeeUpdate
--WHERE Fees.FeeAmount <> Bookings.FeeCharged
I have a table with details of sold cars. Some of these cars have been resold within last 1, 2 or 3 years. The table looks like this:
Car_Type || Car_Reg_No || Sold_Date || Listing_No
Hatch || 23789 || 2017-02-03 11:26 || X6529
Coupe || 16723 || 2016-11-07 09:40 || N8156
Sedan || 35216 || 2016-05-23 10:34 || M8164
Hatch || 23789 || 2016-09-16 04:30 || O7361
Now, I need to query records (cars) which were re-sold within 1 year of their latest sold date and how many times were they sold. So, my output would be like this:
Car_Type || Car_Reg_No || Sold_Count || Latest_Sold_Date
Hatch || 23789 || 2 || 2017-02-03 11:26
In essence, How do I check for re-sold records within a specific time frame of their latest sold date?
You can do this by finding the max, and joining based on your conditions.
declare #TableA table (Car_Type varchar(64)
,Car_Reg_No int
,Sold_Date datetime
,Listing_No varchar(6))
insert into #TableA
values
insert into #TableA
values
('Hatch',23789,'2017-02-03 11:26','X6529'),
('Coupe',16723,'2017-11-07 09:40','N8156'),
('Sedan',35216,'2017-05-23 10:34','M8164'),
('Hatch',23789,'2016-09-16 04:30','O7361'),
('Coupe',16723,'2014-11-07 09:40','N8156')
;with cte as(
select
Car_Type
,Car_Reg_No
,Latest_Sold_Date = max(Sold_Date)
from
#TableA
group by
Car_Type
,Car_Reg_No)
select
a.Car_Type
,a.Car_Reg_No
,Sold_Count = count(b.Listing_No) + 1
,a.Latest_Sold_Date
from cte a
inner join
#TableA b on
b.Car_Reg_No = a.Car_Reg_No
and b.Sold_Date != a.Latest_Sold_Date
and datediff(day,b.Sold_Date,a.Latest_Sold_Date) < 366
--if you want only cars which were sold within last year too, uncomment this
--and datediff(day,a.Latest_Sold_Date,getdate()) < 366
group by
a.Car_Type
,a.Car_Reg_No
,a.Latest_Sold_Date
By my understanding..,
select sd1.Car_Type, sd1.Car_Reg_No,
count(sd1.Car_Reg_No) + 1 'no of sales in last one year', --1 is added because, see the last condition
sd1.Sold_Date 'Last sold date'
from(
select *,ROW_NUMBER() over(partition by Car_Reg_No order by sold_date desc) as rn from #Table) as sd1
join
(select * from #Table) as sd2
on sd1.Car_Type = sd2.Car_Type
and DATEDIFF(dd,sd2.Sold_Date,sd1.Sold_Date) < 366
and sd1.rn = 1
and sd1.Sold_Date <> sd2.Sold_Date -- here last sold is eliminated. so count is added by one.
group by sd1.Car_Type,sd1.Sold_Date, sd1.Car_Reg_No
order by sd1.Car_Reg_No
Problem: I have dated salary information stored in a table. I need to display one result per year. For each year, I want to display the max dated record from the year prior. The problem is that some years don’t have data (salary didn’t change). I need these rows to contain the max record prior to that year (it may be from 2 years before or even 3).
My query right now would work if each row has data… but it doesn’t account for years where there is no data. How can I update this sql to pull the desired results below:
Example of data:
sch_sal_svc.beg_date -------sch_sal_svc.beg_date.per_plan_data
1/1/2007---100
6/1/2007---200
1/1/2008---300
1/1/2011---400
8/1/2011---500
9/1/2012---600
Current Results
1/1/2008---200
1/1/2011---300
1/1/2012---500
Desired Results
1/1/2008---200
1/1/2009---300
1/1/2010---300
1/1/2011---300
1/1/2012---500
SQL:
SELECT
years.control_id,
years.ssn,
ebe.plan_id,
to_number(to_char(years.sal_date,'yyyy')),
null as per_plan_salary,
null as per_vest_hours,
null as per_credsvc_hours,
LEAST(s.rate_1,cl.comp_genl),
null as salary_1,
null as per_comm,
null as per_overtime,
null as per_ncr,
null as salary_2
FROM
sch_sal_svc s
, (select distinct ssn, control_id, TRUNC(beg_date,'YEAR') as sal_date from sch_sal_svc where beg_date > to_date('12/31/1900', 'mm/dd/yyyy')) years
, employee_benefit_elig ebe, compliance_limits cl
WHERE
years.ssn = ebe.ssn
and years.control_id = ebe.control_id
and to_number(to_char(years.sal_date,'yyyy')) = cl.limit_year
and to_number(to_char(years.sal_date,'yyyy')) <= to_number(to_char(sysdate,'yyyy'))
and s.beg_date = (
select max(s2.beg_date) from sch_sal_svc s2
where s2.ssn = years.ssn and s2.control_id = years.control_id
and s2.beg_date <= years.sal_date
)
and s.ssn = years.ssn
and s.control_id = years.control_id
and ebe.benefit_id = 'DB'
and ebe.control_id = 'CLIENT'
and ebe.plan_id in ('100', '200')
CREATE TABLE sch_sal_svc
(
beg_date DATE
, per_plan_data NUMBER
);
INSERT INTO sch_sal_svc VALUES(TO_DATE('01/01/2007', 'DD/MM/YYYY'), 100);
INSERT INTO sch_sal_svc VALUES(TO_DATE('06/01/2007', 'DD/MM/YYYY'), 200);
INSERT INTO sch_sal_svc VALUES(TO_DATE('01/01/2008', 'DD/MM/YYYY'), 300);
INSERT INTO sch_sal_svc VALUES(TO_DATE('01/01/2011', 'DD/MM/YYYY'), 400);
INSERT INTO sch_sal_svc VALUES(TO_DATE('08/01/2011', 'DD/MM/YYYY'), 500);
INSERT INTO sch_sal_svc VALUES(TO_DATE('09/01/2012', 'DD/MM/YYYY'), 600);
SELECT MIN(beg_date) FROM sch_sal_svc;
-- 2007-01-01 00:00:00
SELECT d.r_level + NUMTOYMINTERVAL(1, 'YEAR') AS d_date
, NVL -- the salary must be updated at least once in three years
(
NVL
(
NVL
(
s.per_plan_data
, LAG(s.per_plan_data, 1) OVER (PARTITION BY 1 ORDER BY d.r_level)
)
, LAG(s.per_plan_data, 2) OVER (PARTITION BY 1 ORDER BY d.r_level)
)
, LAG(s.per_plan_data, 3) OVER (PARTITION BY 1 ORDER BY d.r_level)
) AS lag_per_plan_data
FROM
(
SELECT DATE'2006-01-01' + NUMTOYMINTERVAL(LEVEL, 'YEAR') AS r_level -- min beg_date minus 1
FROM DUAL
CONNECT BY
LEVEL < (SELECT TO_CHAR(MAX(beg_date), 'YYYY') - TO_CHAR(MIN(beg_date), 'YYYY') + 2 FROM sch_sal_svc)
) d
LEFT JOIN
(
SELECT beg_date
, per_plan_data
FROM sch_sal_svc
WHERE (beg_date) IN
(
SELECT MAX(beg_date)
FROM sch_sal_svc
GROUP BY
TRUNC(beg_date, 'YYYY')
)
) s
ON d.r_level = TRUNC(s.beg_date, 'YYYY')
;
/*
2008-01-01 00:00:00 200
2009-01-01 00:00:00 300
2010-01-01 00:00:00 300
2011-01-01 00:00:00 300
2012-01-01 00:00:00 500
2013-01-01 00:00:00 600
*/