populating table from different tables - sql

Good day.
I have the following tables:
Order_Header(Order_id {pk}, customer_id {fk}, agent_id {fk}, Order_date(DATE FORMAT))
Invoice_Header (Invoice_ID {pk}, Customer_ID {fk}, Agent_ID{fk}, invoice_Date{DATE FORMAT} )
Stock( Product_ID {pk}, Product_description)
I created a table called AVG_COMPLETION_TIME_FACT and want to populate it with the following values regarding the previous 3 tables:
Product_ID
Invoice_month
Invoice_Year
AVG_Completion_Time (Invoice_date - Order_date)
I have the following code that doesn't work:
INSERT INTO AVG_COMPLETION_TIME_FACT(
SELECT PRODUCT_ID, EXTRACT (YEAR FROM INVOICE_DATE), EXTRACT (MONTH FROM INVOICE_DATE), (INVOICE_DATE - ORDER_DATE)
FROM STOCK, INVOICE_HEADER, ORDER_HEADER
GROUP BY PRODUCT_ID, EXTRACT (YEAR FROM INVOICE_DATE), EXTRACT (MONTH FROM INVOICE_DATE)
);
I want to group it by the product_id, year of invoice and month of invoice.
Is this possible?
Any advice would be much appreciated.
Regards

Short answer: it may be possible - if your database contains some more columns that are needed for writing the correct query.
There are several problems, apart from the syntactical ones. When we create some test tables, you can see that the answer you are looking for cannot be derived from the columns you have provided in your question. Example tables (Oracle 12c), all PK/FK constraints omitted:
-- 3 tables, similar to the ones described in your question,
-- including some test data
create table order_header (id, customer_id, agent_id, order_date )
as
select 1000, 100, 1, date'2018-01-01' from dual union all
select 1001, 100, 2, date'2018-01-02' from dual union all
select 1002, 100, 3, date'2018-01-03' from dual
;
create table invoice_header ( id, customer_id, agent_id, invoice_date )
as
select 2000, 100, 1, date'2018-02-01' from dual union all
select 2001, 100, 2, date'2018-03-11' from dual union all
select 2002, 100, 3, date'2018-04-21' from dual
;
create table stock( product_id, product_description)
as
select 3000, 'product3000' from dual union all
select 3001, 'product3001' from dual union all
select 3002, 'product3002' from dual
;
If you join the tables as you have done it (using a cross join), you will see that you get more rows than expected ... But: Neither the invoice_header table, nor the order_header table contains any PRODUCT_ID data. Thus, we cannot tell which product_ids are associated with the stored order_ids or invoice_ids.
select
product_id
, extract( year from invoice_date )
, extract( month from invoice_date )
, invoice_date - order_date
from stock, invoice_header, order_header -- cross join -> too many rows in the resultset!
-- group by ...
;
...
27 rows selected.
For getting your query right, you should probably write INNER JOINs and conditions (keyword: ON). If we try to do this with your original table definitions (as provided in your question) you will see that we cannot join all 3 tables, as they do not contain all the columns needed: PRODUCT_ID (table STOCK) cannot be associated with ORDER_HEADER or INVOICE_HEADER.
One column that these 2 tables (ORDER_HEADER and INVOICE_HEADER) do have in common is: customer_id, but that's not enough for answering your question. However, we can use it for demonstrating how you could code the JOINs.
select
-- product_id
IH.customer_id as cust_id
, OH.id as OH_id
, IH.id as IH_id
, extract( year from invoice_date ) as year_
, extract( month from invoice_date ) as month_
, invoice_date - order_date as completion_time
from invoice_header IH
join order_header OH on IH.customer_id = OH.customer_id
-- the stock table cannot be joined at this stage
;
Missing columns:
Please regard the following just as "proof of concept" code. Assuming that somewhere in your database, you have tables that have columns that {1} link STOCK and ORDER_HEADER (name here: STOCK_ORDER) and {2} link ORDER_HEADER and INVOICE_HEADER (name here: ORDER_INVOICE), you could actually get the information you want.
-- each ORDER_HEADER is mapped to multiple product_ids
create table stock_order
as
select S.product_id, OH.id as oh_id -- STOCK and ORDER_HEADER
from stock S, order_header OH ; -- cross join, we use all possible combinations here
select oh_id, product_id
from stock_order
order by OH_id
;
PRODUCT_ID OH_ID
---------- ----------
3000 1000
3000 1001
3000 1002
3001 1000
3001 1001
3001 1002
3002 1000
3002 1001
3002 1002
9 rows selected.
-- each INVOICE_HEADER mapped to a single ORDER_HEADER
create table order_invoice ( order_id, invoice_id )
as
select 1000, 2000 from dual union all
select 1001, 2001 from dual union all
select 1002, 2002 from dual
;
For querying, make sure that you code the correct JOIN conditions (ON ...) eg
-- example query. NOTICE: conditions in ON ...
select
S.product_id
, IH.customer_id as cust_id
, OH.id as OH_id
, IH.id as IH_id
, extract( year from invoice_date ) as year_
, extract( month from invoice_date ) as month_
, invoice_date - order_date as completion_time
from invoice_header IH
join order_invoice OI on IH.id = OI.invoice_id -- <- new "link" table
join order_header OH on OI.order_id = OH.id
join stock_order SO on OH.id = SO.OH_id -- <- new "link" table
join stock S on S.product_id = SO.product_id
;
Now you can add the GROUP BY, and SELECT only the columns you need. Combined with an INSERT, you should write something like ...
-- example avg_completion_time_fact table.
create table avg_completion_time_fact (
product_id number
, year_ number
, month_ number
, avg_completion_time number
) ;
insert into avg_completion_time_fact ( product_id, year_, month_, avg_completion_time )
select
S.product_id
, extract( year from invoice_date ) as year_
, extract( month from invoice_date ) as month_
, avg( invoice_date - order_date ) as avg_completion_time
from invoice_header IH
join order_invoice OI on IH.id = OI.invoice_id
join order_header OH on OI.order_id = OH.id
join stock_order SO on OH.id = SO.OH_id
join stock S on S.product_id = SO.product_id
group by S.product_id, extract( year from invoice_date ), extract( month from invoice_date )
;
The AVG_COMPLETION_TIME_FACT table now contains:
SQL> select * from avg_completion_time_fact order by product_id ;
PRODUCT_ID YEAR_ MONTH_ AVG_COMPLETION_TIME
---------- ---------- ---------- -------------------
3000 2018 3 68
3000 2018 4 108
3000 2018 2 31
3001 2018 3 68
3001 2018 2 31
3001 2018 4 108
3002 2018 3 68
3002 2018 4 108
3002 2018 2 31
It is not completely clear what the final query for your database (or schema) will look like, as we don't know the definitions of all the tables it contains. However, if you apply the techniques and stick to the syntax of the examples, you should be able to obtain the required results. Best of luck!

Related

On same row: last purchase quantity + date, and total quantity in stock (several stock places) - SQL server

I am trying to get the following result in SQL server:
From the purchase order rows, last purchase quantity + date from all item codes in the order rows table and from the warehouse table amount in stock for the item codes I get from the rows table.
Order rows:
ORDER_DATE ITEM_CODE QTY
2019-03-01 A 5
2019-03-02 A 3
2019-03-05 A 4
2019-03-03 B 3
2019-03-04 B 10
Warehouse:
ITEM_CODE INSTOCK STOCKPLACE
A 10 VV
A 3 LP
A 8 XV
B 5 VV
B 15 LP
Wanted result (Latest order date, latest order qty and total in stock):
ORDER_DATE ITEM_CODE QTY INSTOCK
2019-03-05 A 4 21
2019-03-04 B 10 20
I have tried some queries but only failed. I have a steep learning curve ahead of me :) Thanks in advance for all the help!
Here is one method:
select o.*, wh.*
from (select wh.item_code, sum(wh.instock) as instock
from warehouse wh
group by wh.item_code
) wh outer apply
(select top (1) o.*
from orders o
where o.item_code = wh.item_code
order by o.order_date desc
) o;
You can use row_number() with apply :
select t.*, wh.instock
from (select o.*, row_number () over (partition by item_code order by o.order_date desc) as seq
from Order o
) t cross apply
( select sum(wh.instock) as instock
from warehouse wh
where wh.item_code = t.item_code
) wh
where t.seq = 1;
Your Orders aren't identified with a unique ID, and therefore if multiple Orders were to coincide on the same date, you have no way of telling which is the most recent order on that day.
Anyway, assuming that the database you posted is correct and an Order date + Item Code combines to form a unique key, you could use grouping and some CTE to get the desired output as follows.
;WITH MostRecentOrders (ITEM_CODE, ORDER_DATE)
AS (
SELECT
O.ITEM_CODE
, MAX(O.ORDER_DATE) AS ORDER_DATE
FROM
#Order O
GROUP BY ITEM_CODE
)
SELECT
O.ORDER_DATE
, O.ITEM_CODE
, O.QTY
, SUM(WH.INSTOCK) AS INSTOCK
FROM
#Warehouse WH
INNER JOIN #Order O ON O.ITEM_CODE = WH.ITEM_CODE
INNER JOIN MostRecentOrders MRO ON MRO.ITEM_CODE = O.ITEM_CODE
AND MRO.ORDER_DATE = O.ORDER_DATE
GROUP BY
O.ORDER_DATE
, O.ITEM_CODE
, O.QTY
ORDER BY O.ITEM_CODE

Sum inventory item groups

I'm trying to sum all starting inventories and cap inventory amounts of product groups. Initial output will give starting inventories and caps of specific products, but I'd like to roll up the total starting inventory into the product group level.
This is in Oracle SQL. I've tried removing the product category and adding the SUM() function to the starting inventory, but it still is not rolling up the sum to the group level. There is a SELECT statement in the FROM clause... maybe there is an issue there???
SELECT OPRVIEW_OBSERVATION_DATE,
OPRVIEW.LOOKUP_CODE OPRVIEW_LOOKUP_CODE,
HSALE001V.GROUPBUD HSALE001V_GROUPBUD,
sum(HSALE001V.CAP) HSALE001V_CAP,
sum(OPRVIEW.START_INV) OPRVIEW_START_INV
FROM (SELECT h.observation_date,
loc.lookup_code,
inv.start_inv,
inv.product
FROM apps.nlas_inventory inv,
apps.nlas_header h,
apps.nlas_location loc
WHERE inv.header_id = h.id
AND loc.id = h.location_id) OPRVIEW
INNER JOIN (select rtrim(ltrim(gnmcu)) as siteBUD,
rtrim(ltrim(GNALPH1)) as itemBUD,
GNDC as StatusBUD,
GNAN25 as CAP,
GNALPH2 as GroupBUD
from hsale001) HSALE001V
ON OPRVIEW.LOOKUP_CODE = HSALE001V.SITEBUD
AND OPRVIEW.PRODUCT = HSALE001V.ITEMBUD
WHERE ( ( ( OPRVIEW.OBSERVATION_DATE BETWEEN TO_DATE('2019-08-07 00 00 00', 'yyyy-MM-dd hh24:mi:ss') AND TO_DATE('2019-08-07 23 59 59', 'yyyy-MM-dd hh24:mi:ss') )
AND ( OPRVIEW.LOOKUP_CODE IN ( '123' ) ) )
AND ( HSALE001V.STATUSBUD IN ( 'CA' ) ) )
GROUP BY FLOOR(TO_NUMBER(TO_CHAR(OPRVIEW.OBSERVATION_DATE, 'yyyyddd'))) - 1900000,
OPRVIEW.LOOKUP_CODE,
HSALE001V.GROUPBUD,
HSALE001V.CAP,
OPRVIEW.START_INV
Current output (lists out each product per line... product is not displayed):
DATE SITE GROUP CAP INVENTORY
119219 123 2 0 3778
119219 123 2 24000 23165
Desired output:
DATE SITE GROUP CAP INVENTORY
119219 123 2 0 26943
As also mentioned in the comment, there is problem with group by. But there are last 2 columns which do not require in group by.
SELECT OPRVIEW_OBSERVATION_DATE,
OPRVIEW.LOOKUP_CODE OPRVIEW_LOOKUP_CODE,
HSALE001V.GROUPBUD HSALE001V_GROUPBUD,
sum(HSALE001V.CAP) HSALE001V_CAP,
sum(OPRVIEW.START_INV) OPRVIEW_START_INV
FROM (SELECT h.observation_date,
loc.lookup_code,
inv.start_inv,
inv.product
FROM apps.nlas_inventory inv,
apps.nlas_header h,
apps.nlas_location loc
WHERE inv.header_id = h.id
AND loc.id = h.location_id) OPRVIEW
INNER JOIN (select rtrim(ltrim(gnmcu)) as siteBUD,
rtrim(ltrim(GNALPH1)) as itemBUD,
GNDC as StatusBUD,
GNAN25 as CAP,
GNALPH2 as GroupBUD
from hsale001) HSALE001V
ON OPRVIEW.LOOKUP_CODE = HSALE001V.SITEBUD
AND OPRVIEW.PRODUCT = HSALE001V.ITEMBUD
WHERE ( ( ( OPRVIEW.OBSERVATION_DATE BETWEEN TO_DATE('2019-08-07 00 00 00', 'yyyy-MM-dd hh24:mi:ss') AND TO_DATE('2019-08-07 23 59 59', 'yyyy-MM-dd hh24:mi:ss') )
AND ( OPRVIEW.LOOKUP_CODE IN ( '123' ) ) )
AND ( HSALE001V.STATUSBUD IN ( 'CA' ) ) )
GROUP BY FLOOR(TO_NUMBER(TO_CHAR(OPRVIEW.OBSERVATION_DATE, 'yyyyddd'))) - 1900000,
OPRVIEW.LOOKUP_CODE,
HSALE001V.GROUPBUD;
--HSALE001V.CAP, -- not required
--OPRVIEW.START_INV -- not required
You need only columns in the group by using which you need your result to be grouped as also its name suggests.
Aggregate functions are mostly used on non grouped columns.
Note: CAP will be 24000 using this query.
Cheers!!

Oracle - Getting the latest list price from list table and append it another product table

I have a list price table, where product prices keeps on changing and another table Product table , which has the entry of dates when the product was sold and at what price.
I want to compare the price of the product on or before that date ( when it was sold) and put that list price in my product table along with the selling price.
Table like :
list price , Valid from ,valid to product
23 , jul 7 , july 15, X
24 , jul 20 , july 30,X
25 , aug 5 , aug 30,X
20,sep 5,sep 26,X
Product table :
Product , Selling price , As of date
X , 24 , jul 10
X,39, jul 30
x,40, aug 28
I wish to append a column(ListPrice) to my Product table using the closest dates
to AS_OF_DATE column and List price on that date in Price Table.
select Product.*, price.list_price from
(select '2017-07-20 12:00:00'::timestamp as
valid_from,select '2017-07-20 12:00:00'::timestamp as valid_from) price
left join (select '2017-07-20 12:15:00'::timestamp as ts, true as val
)product on product.ts >= price.valid_from and b.ts < price.valid_to;
Please suggest the way to to do it in oracle.
Just use a join
select p.*, lp.*
from products p left join
listprices lp
on p.product = lp.product and
p.asofdate >= lp.validfrom and
p.asofdate <= lp.validto;
If you don't want to take the validto dates into account . . .
select p.*,
(select max(lp.price) keep (dense_rank first order by lp.validfrom desc)
from listprices lp
where p.product = lp.product and
p.asofdate >= lp.validfrom
) as listprice
from products p ;

Getting rid of grouping field

Is there a safe way to not have to group by a field when using an aggregate in another field? Here is my example
SELECT
C.CustomerName
,D.INDUSTRY_CODE
,CASE WHEN D.INDUSTRY_CODE IN ('003','004','005','006','007','008','009','010','017','029')
THEN 'PM'
WHEN UPPER(CustomerName) = 'ULINE INC'
THEN 'ULINE'
ELSE 'DR'
END AS BU
,ISNULL((SELECT SUM(GrossAmount)
where CONVERT(date,convert(char(8),InvoiceDateID )) between DATEADD(yy, DATEDIFF(yy, 0, GETDATE()) - 1, 0) and DATEADD(year, -1, GETDATE())),0) [PREVIOUS YEAR GROSS]
FROM factMargins A
LEFT OUTER JOIN dimDate B ON A.InvoiceDateID = B.DateId
LEFT OUTER JOIN dimCustomer C ON A.CustomerID = C.CustomerId
LEFT OUTER JOIN CRCDATA.DBO.CU10 D ON D.CUST_NUMB = C.CustomerNumber
GROUP BY
C.CustomerName,D.INDUSTRY_CODE
,A.InvoiceDateID
order by CustomerName
before grouping I was only getting 984 rows but after grouping by the A.InvoiceDateId field I am getting over 11k rows. The rows blow up since there are multiple invoices per customer. Min and Max wont work since then it will pull data incorrectly. Would it be best to let my application (crystal) get rid of the extra lines? Usually I like to have my base data be as close as possible to how the report will layout if possible.
Try moving the reference to InvoiceDateID to within an aggregate function, rather than within a selected subquery's WHERE clause.
In Oracle, here's an example:
with TheData as (
select 'A' customerID, 25 AMOUNT , trunc(sysdate) THEDATE from dual union
select 'B' customerID, 35 AMOUNT , trunc(sysdate-1) THEDATE from dual union
select 'A' customerID, 45 AMOUNT , trunc(sysdate-2) THEDATE from dual union
select 'A' customerID, 11000 AMOUNT , trunc(sysdate-3) THEDATE from dual union
select 'B' customerID, 12000 AMOUNT , trunc(sysdate-4) THEDATE from dual union
select 'A' customerID, 15000 AMOUNT , trunc(sysdate-5) THEDATE from dual)
select
CustomerID,
sum(amount) as "AllRevenue"
sum(case when thedate<sysdate-3 then amount else 0 end) as "OlderRevenue",
from thedata
group by customerID;
Output:
CustomerID | AllRevenue | OlderRevenue
A | 26070 | 26000
B | 12035 | 12000
This says:
For each customerID
I want the sum of all amounts
and I want the sum of amounts earlier than 3 days ago

How to identify to correct row

I have two tables, namely Price List (Table A) and Order Record (Table B)
Table A
SKU Offer Date Amt
AAA 20120115 22
AAA 20120223 24
AAA 20120331 25
AAA 20120520 28
Table B
Customer SKU Order Date
A001 AAA 20120201
B001 AAA 20120410
C001 AAA 20120531
I have to retrieve the correct pricing for each customer based on the order date. The expected output should be like this:-
Customer SKU Order Date Amt
A001 AAA 20120201 22
B001 AAA 20120410 25
C001 AAA 20120531 28
Thanks.
A left join (or NOT EXISTS subquery) can be used to ensure that the join between the two tables uses the "most recent" row from the prices table that is dated on or before the order date. I assume that's the relationship between the tables that you want to achieve:
Setup:
create table Prices (
SKU char(3) not null,
OfferDate date not null,
Amt int not null
)
go
insert into Prices (SKU, OfferDate, Amt) values
('AAA','20120115', 22),
('AAA','20120223', 24),
('AAA','20120331', 25),
('AAA','20120520', 28)
go
create table Orders (
Customer char(4) not null,
SKU char(3) not null,
OrderDate date not null
)
go
insert into Orders (Customer, SKU, OrderDate) values
('A001','AAA','20120201'),
('B001','AAA','20120410'),
('C001','AAA','20120531')
go
Query:
select
o.*, /* TODO - Explicit columns */
p.Amt
from
Orders o
inner join
Prices p
on
o.SKU = p.SKU and
o.OrderDate >= p.OfferDate
left join
Prices p_later
on
o.SKU = p_later.SKU and
o.OrderDate >= p_later.OfferDate and
p_later.OfferDate > p.OfferDate
where
p_later.SKU is null
Next time, do put up what u have tried....
anyways, here is your answer! try...
Select X.Customer , X.SKU , X.OrderDate , Y.Amt from B as X INNER JOIN A as Y ON X.Order Date= Y. Offer Date
good luck...
SELECT o.Customer, o.SKU, o.[Order Date],
(SELECT TOP 1 l.Amt
FROM PriceList l
WHERE l.[Offer Date] <= o.[Order Date] AND o.SKU = l.SKU
ORDER BY l.[Offer Date] DESC) AS Amount
FROM
Orders o
Some things may differ based on database support