We are migrating our PRODUCTION database from MSSQL to Oracle 11G. Part of this process is to recreate some existing stored procedures from TSQL to Oracle SQL. Being new to Oracle, I have a question on best practice in a specific situation.
Essentially, I have a table that summarizes data from a sales transaction table. The basic layout of the table (F5542HIS) is as follows (* indicates index):
*NSAN8 (sold_to)
*NSSHAN (ship_to)
*NSMCU (sold_from)
*NSITM (item)
*NSCTRY (Century)
*NSYR (year)
*NSMNTH (month)
*NSDCTO (Order Type)
*NSLNTY (Order Line Sales Type)
NSAEXP (Total Extended Price)
NSSOQS (Total Units Shipped)
NSECST (Total Extended Cost)
On my first pass of the script, I populate the table with the primary index values (indicated by the * above).
On the second pass, I need to do an update to this table to populate the summation values from my transaction tables. The transaction tables are the F4211 (open orders) and F42119 (order history), so a union is required.
So, I need to do an update based on the following select statement which returns the proper data:
select sum(cast(sdaexp as numeric)) sumaexp,
sum(cast(sduorg as numeric)) sumuorg,
sum(cast(sdecst as numeric)) sumecst
from PRODDTA.F4211 a inner join proddta.f5542his b
on(a.sdan8 = b.nsan8 and
a.sdshan = b.nsshan and
a.sditm = b.nsitm and
a.sdlnty = b.nslnty and
a.sddcto = b.nsdcto and
a.sdlnty = b.nslnty and
ltrim(rtrim(a.sdmcu)) = ltrim(rtrim(b.nsmcu)) )
where sdtrdj >= 108001 and
sdtrdj <= 108031 and
group by sdaexp, sduorg, sdecst
UNION
select sum(cast(sdaexp as numeric)) sumaexp,
sum(cast(sduorg as numeric)) sumuorg,
sum(cast(sdecst as numeric)) sumecst
from PRODDTA.F4211 a inner join proddta.f5542his b
on(a.sdan8 = b.nsan8 and
a.sdshan = b.nsshan and
a.sditm = b.nsitm and
a.sddcto = b.nsdcto and
a.sdlnty = b.nslnty and
ltrim(rtrim(a.sdmcu)) = ltrim(rtrim(b.nsmcu)) )
where sdivd >= 108001 and
sdivd <= 108031 and
group by sdaexp, sduorg, sdecst
Notice that the SDTRDJ (transaction date) and also the SDIVD (Invoice date) are using the values >= 108001 and <= 108031. These are JULIAN DAYS (not julian dates). It is a strange method of retaining dates, but it works. 108001 = Jan 1, 2008, and 108031 = Jan 31, 2008.
Also, the F5542HIS table will have a WHERE clause on the update for this example where the NSCTRY (Century) = 20, NSYR (Year) = 8, and NSMNTH (Month) = 1. So essentially, the structure could be inthe form:
UPDATE f5542HIS
SET nsaexp=sumaexp, nsuorg-sumuorg, nsecst=sumecst from<union>
where nsctry=20 and nsyr=8 and nsmnth=1
I have been looking at the SQL Update and also the SQL MERGE functions. Can someone help me define the best practice for formatting this statement into either an UPDATE or MERGE that will work here?
Related
I have a table containing many rows about financial data. Colums are as follows
Unixtime,open,high,low,close,timeframe,sourceId.
Given two assets with same timeframe but different sourceId, how to show a table which has
unixtime, Asset1open/asset2open,Asset1close/asset2close as columns?
Every resulting row should be the result of prices that have the same unixtime, and should be ordered by unixtime asc order.
How to do it with a self join?
You don't mention the specific database, so I'll assume this is for Sybase.
You can do:
select
a.unixtime,
a.open / b.open,
a.close / b.close
from t a
join t b on a.unixtime = b.unixtime and a.timeframe = b.timeframe
where a.sourceid = 123
and b.sourceid = 456
order by a.unixtime
I have a sql db2 code that get information on a existing data table and I want to modify it to not download the data starting from 2020-02-01, instead just update this table everyday. Please help me to modify this script not to retrieve the whole data over and over again just to update but keeps on updating only the new data everyday.
create table public.fc_TDPMean_By_DIM2_EtchDate as
select DIM2_EtchDate, Model, tool_id, avg(TDPower) as TDPower_Mean, count(slider_id)
from (select distinct a.slider_id, trunc(a.test_date_time), left(a.product_id,2) as Model, a.wafer_id, a.row_number,
a.column_number, a.x_coordinate, a.y_coordinate, a.error_code, a.grade, a.bin,
a.TFCTDPWR as TDPower, b.job_number, trunc(c.transaction_date_time) as DIM2_EtchDate, c.tool_id
from ah.param_jade_wide a left join ah.param_lap_summary b on a.wafer_id = b.wafer_id and a.row_number = b.row_number
left join ah.his_job c on c.job_number = b.job_number
where c.transaction_date_time > '2020-02-01'
and left(a.product_id,2) in ('L2','L3','L8','C3','C2','V8')
and b.source_system_code in ('MFG2.SLDR.LAPRUN')
and c.operation_id in ('545600')
and a.retest_number = 0
and a.class_description in ('PROD')
and not c.tool_id = 0 and not c.tool_id in ('') )
group by DIM2_EtchDate, Model, tool_id;
commit;
As You want to add a new "day" entry/entries every day (as DIM2_EtchDate == DATE(c.transaction_date_time) is a part of the summary table), and if You are OK with not selecting "today's" entries, You can just use a simple INSERT for "yesterdays and before" entries, run every day like this:
INSERT INTO public.fc_TDPMean_By_DIM2_EtchDate
SELECT DIM2_EtchDate, Model, tool_id, avg(TDPower) AS TDPower_Mean, count(slider_id)
FROM (SELECT DISTINCT a.slider_id, LEFT(a.product_id,2) as Model,
a.TFCTDPWR AS TDPower, TRUNC(c.transaction_date_time) as DIM2_EtchDate, c.tool_id
-- (I got rid of not used columns.)
FROM ah.param_jade_wide a
LEFT JOIN ah.param_lap_summary b ON (a.wafer_id, a.row_number) = (b.wafer_id, b.row_number)
LEFT JOIN ah.his_job c ON c.job_number = b.job_number
WHERE c.transaction_date_time > '2020-02-01' --MAX(DIM2_EtchDate)
AND c.transaction_date_time < CURRENT DATE
AND LEFT(a.product_id,2) IN ('L2','L3','L8','C3','C2','V8')
AND b.source_system_code IN ('MFG2.SLDR.LAPRUN')
AND c.operation_id IN ('545600')
AND a.retest_number = 0
AND a.class_description IN ('PROD')
AND NOT c.tool_id = 0 AND NOT c.tool_id in ('')
)
GROUP BY DIM2_EtchDate, Model, tool_id
-- WITH appropriate isolation
;
(I haven't tested it. This should work if c.transaction_date_time is DATE. If it is TIMESTAMP, then c.transaction_date_time > '2020-02-01' changes to c.transaction_date_time >= TIMESTAMP('2020-02-01', '00:00') + 1 DAY.)
If You would want to update it more often than once per day, having also incomplete data for today, it could be done using MERGE. If so, just tell me, I could manage to write a MERGE for it. (Or You could, that would be better.)
I have a query
SELECT
*
FROM
mgr.MF_AGREEMENT_LGR TABLE1
INNER JOIN
(SELECT
MAX(VALUE_DATE) AS VALUE_DATE,
REGISTRATION_NO AS REGISTRATION_NO
FROM
mgr.MF_AGREEMENT_LGR
GROUP BY
REGISTRATION_NO) AS TABLE2 ON TABLE1.REGISTRATION_NO = TABLE2.REGISTRATION_NO
WHERE
TABLE1.VALUE_DATE = TABLE2.VALUE_DATE
AND TABLE1.TRX_CODE = 'LCLR'
ORDER BY
TABLE1.REGISTRATION_NO
This returns the rows with the latest date for each REGISTRATION_CODE. Some have like three or more results for each REGISTRATION_CODE because it has more than one transaction on the same date.
Also, each row has its DOC_NO field.
My question is, how am I going to get only one row from each REGISTRATION_CODE with the highest DOC_NO.
By the way, DOC_NO is a varchar.
Example value for this field is: Amort 1, Amort 12, Amort 5
If those examples are in one REGISTRATION_CODE, I only need the row with the highest amort which is Amort 12.
I am using a SQL Server 2000.
SQL Server 2000 has not been supported in years. You really should upgrade to supported software.
You can get what you want with not exists:
SELECT al.*
FROM mgr.MF_AGREEMENT_LGR al
WHERE NOT EXISTS (SELECT 1
FROM mgr.MF_AGREEMENT_LGR al2
WHERE al2.registration_no = al.registration_no and
(al2.date > al2.date or
al2.date = al.date and al2.DOC_NO > al.DOC_NO
)
) AND
al.TRX_CODE = 'LCLR';
You probably want the condition on 'LCLR' in the subquery as well. However, that is not in your original query, so I'm leaving it out.
I'm trying to avoid using straight up SQL in my Rails app, but need to do a quite large version of this:
SELECT ds.product_id,
( SELECT SUM(units) FROM daily_sales WHERE (date BETWEEN '2015-01-01' AND '2015-01-08') AND service_type = 1 ) as wk1,
( SELECT SUM(units) FROM daily_sales WHERE (date BETWEEN '2015-01-09' AND '2015-01-16') AND service_type = 1 ) as wk2
FROM daily_sales as ds group by ds.product_id
I'm sure it can be done, but i'm struggling to write this as an active record statement. Can anyone help?
If you must do this in a single query, you'll need to write some SQL for the CASE statements. The following is what you need:
ranges = [ # ordered array of all your date-ranges
Date.new(2015, 1, 1)..Date.new(2015, 1, 8),
Date.new(2015, 1, 9)..Date.new(2015, 1, 16)
]
overall_range = (ranges.first.min)..(ranges.last.max)
grouping_sub_str = \
ranges.map.with_index do |range, i|
"WHEN (date BETWEEN '#{range.min}' AND '#{range.max}') THEN 'week#{i}'"
end.join(' ')
grouping_condition = "CASE #{grouping_sub_str} END"
grouping_columns = ['product_id', grouping_condition]
DailySale.where(date: overall_range).group(grouping_columns).sum(:units)
That will produce a hash with array keys and numeric values. A key will be of the form [product_id, 'week1'] and the value will be the corresponding sum of units for that week.
Simplify your SQL to the following and try converting it..
SELECT ds.product_id,
, SUM(CASE WHEN date BETWEEN '2015-01-01' AND '2015-01-08' AND service_type = 1
THEN units
END) WK1
, SUM(CASE WHEN date BETWEEN '2015-01-09' AND '2015-01-16' AND service_type = 1
THEN units
END) WK2
FROM daily_sales as ds
group by ds.product_id
Every rail developer sooner or later hits his/her head against the walls of Active Record query interface just to find the solution in Arel.
Arel gives you the flexibility that you need in creating your query without using loops, etc. I am not going to give runnable code rather some hints how to do it yourself:
We are going to use arel_tables to create our query. For a model called for example Product, getting the Arel table is as easy as products = Product.arel_table
Getting sum of a column is like daily_sales.project(daily_sales[:units].count).where(daily_sales[:date].gt(BEGIN_DATE).where(daily_sales[:date].lt(END_DATE). You can chain as many wheres as you want and it will be translated into SQL ANDs.
Since we need to have multiple sums in our end result you need to make use of Common Table Expressions(CTE). Take a look at docs and this answer for more info on this.
You can use those CTEs from step 3 in combination with group and you are done!
I have 2 tables: TBL_EQUIPMENTS and TBL_PROPOSAL.
TBL_PROPOSAL has 3 important columns:
id_proposal
date
discount
TBL_EQUIPMENTS has:
id_equipment
id_proposal
unit_price
quantity
Now I want to know how much (in €) is my proposals for this year, let's say:
For each TBL_PROPOSAL.date > "2013-01-01" I want to use the formula:
result = (TBL_EQUIPMENTS.unit_price * TBL_EQUIPMENTS.quantity) * (100 - TBL_PROPOSAL.discount)
I can do this with one SQL statement?
Yes you can:
select e.unit_price * e.quantity) * (100 - p.discount)
from tbl_Proposal p join
tbl_Equipments e
on p.id_Proposal = e.id_proposal
where date >= '2013-01-01'
The basic syntax is for a join. The p and e are called table aliases. They make the query easier to read (the full table names are rather bulky).
Date operations differ among databases. The last statement should work in most databases. However, you might try one of the following as well:
where year(date) = 2013
where extract(year from date) = 2013