Optimized query for a subquery in sql - sql

I made a query to get the inventory of products as follows:
Select
b.ProductID, c.ProductName,
(Select
Case
When SUM(Qty) IS NULL
then 0
else SUM(Qty)
end
from
InvoiceDetails
where
ProductID = b.ProductID) as Sold,
(Select
Case
When SUM(QtyReceive) IS NULL
then 0
else SUM(QtyReceive)
end
from
PurchaseOrderDetails
where
ProductID = b.ProductID) as Stocks,
((Select
Case
When SUM(QtyReceive) IS NULL
then 0
else SUM(QtyReceive)
end
from
PurchaseOrderDetails
where
ProductID = b.ProductID) -
(Select
Case
When SUM(Qty) IS NULL
then 0
else SUM(Qty)
end
from
InvoiceDetails
where
ProductID = b.ProductID)) as RemainingStock
from
InvoiceDetails a
Right join
PurchaseOrderDetails b on a.ProductID = b.ProductID
Inner join
Products c on b.ProductID = c.ProductID
Group By
b.ProductID, c.ProductName
This query returns the data that I want, and it runs fine in my desktop, but when I deploy the application that runs this query on a lower specs laptop, it is really slow and causes the laptop to hang. I need some help on how to optimize the query or maybe change it to make it more efficient... thanks in advance
This are the data of my InvoiceDetails table
Data From my PurchaseOrderDetails table
Data from Products table

So I've taken out your subqueries in the select, I don't think these were necessary at all. I've also moved around your joins and given better aliases to the tables;
SELECT
b.ProductID,
c.ProductName,
ISNULL(SUM(id.Qty),0) as Sold,
ISNULL(SUM(pod.QtyReceive),0) as Stocks,
ISNULL(SUM(pod.QtyReceive),0) - ISNULL(SUM(id.Qty),0) as RemainingStock
FROM PurchaseOrderDetails pod
INNER JOIN Products pr
ON pr.ProductID = pod.ProductID
LEFT JOIN InvoiceDetails id
ON id.ProductID = pod.ProductID
GROUP BY
pod.ProductID, pr.ProductName
You were already joining those two tables so you don't need subqueries in the select at all. I've also wrapped the SUM in ISNULL to ensure there are no NULL errors.
I'd suggest using the SET STATISTICS TIME,IO ON at the beginning of your code (with an OFF command at the end). Then copy all of the text from your 'messages' tab into statisticsparser.com. Do this for both queries and compare, check the total CPU time and the logical reads, you want these both lower for better performance. I'm betting your logical reads will drop significantly with this new query.
EDIT
OK, I've put together a new query based upon your sample data. I've only used the fields that we actually need for this query so that it's simpler for this example.
Sample Data
CREATE TABLE #InvoiceDetails (ProductID int, Qty int)
INSERT INTO #InvoiceDetails (ProductID,Qty)
VALUES (3,50),(1,0),(2,1),(1,12),(2,1),(3,1),(1,1),(2,1),(1,1),(2,1)
CREATE TABLE #PurchaseOrderDetails (ProductID int, Qty int)
INSERT INTO #PurchaseOrderDetails (ProductID, Qty)
VALUES (1,100),(2,20),(4,10),(1,12),(5,12),(4,12),(3,12),(2,20),(3,20),(4,20),(5,20)
CREATE TABLE #Products (ProductID int, ProductName varchar(20))
INSERT INTO #Products (ProductID, ProductName)
VALUES (1,'Sample Product'),(2,'DYE INK CYAN'),(3,'test Product 1'),(4,'test Product 2'),(5,'test Product 3'),(1004,'TESTING PRODUCT')
For this, here is the output of your original query
ProductID ProductName Sold Stocks RemainingStock
1 Sample Product 14 112 98
2 DYE INK CYAN 4 40 36
3 test Product 1 51 32 -19
4 test Product 2 0 42 42
5 test Product 3 0 32 32
This is the re-written query that I've used. Note, there are no subqueries within the SELECT statement, they're within the joins as they should be. Also see that as we're aggregating in the subqueries we don't need to do this in the outer query too.
SELECT
pod.ProductID,
pr.ProductName,
ISNULL(id.Qty,0) as Sold,
ISNULL(pod.Qty,0) as Stocks,
ISNULL(pod.Qty,0) - ISNULL(id.Qty,0) as RemainingStock
FROM #Products pr
INNER JOIN (SELECT ProductID, SUM(Qty) Qty FROM #PurchaseOrderDetails GROUP BY ProductID) pod
ON pr.ProductID = pod.ProductID
LEFT JOIN (SELECT ProductID, SUM(Qty) Qty FROM #InvoiceDetails GROUP BY ProductID) id
ON id.ProductID = pr.ProductID
And this is the new output
ProductID ProductName Sold Stocks RemainingStock
1 Sample Product 14 112 98
2 DYE INK CYAN 4 40 36
3 test Product 1 51 32 -19
4 test Product 2 0 42 42
5 test Product 3 0 32 32
Which matches your original query.
I'd suggest trying this query on your machines and seeing which performs better, try the STATISTICS TIME,IO command I mentioned previously.

You grouped by b.ProductID, c.ProductName then you could use aggregate function to calculate.
And create indexes in your table to improve performance.
Select
b.ProductID, c.ProductName,
SUM(isnull(a.Qty,0)) as Sold,
SUM(b.QtyReceive) as Stocks,
SUM(b.QtyReceive) - SUM(isnull(a.Qty,0)) as RemainingStock
from
PurchaseOrderDetails b
LEFT JOIN InvoiceDetails a on a.ProductID = b.ProductID
INNER JOIN Products c on b.ProductID = c.ProductID
Group By
b.ProductID, c.ProductName

Can you try this? (I wrote without testing, as you didn't post sample data nor create table). Please check it and use as a starting point. Compare results from your query and this and compare execution plan. Analysis of performances requires "some" knowledge of Sql and ability to consider several things (eg. how many rows, are there indexes, using of execution plan and statistics, etc.)
SELECT C.PRODUCTID
,C.PRODUCTNAME
,COALESCE(D.QTY_SOLD,0) AS QTY_SOLD
,COALESCE(E.QTY_STOCKS,0) AS QTY_STOCKS
,COALESCE(E.QTY_STOCKS,0)-COALESCE(D.QTY_SOLD,0) AS REMAININGSTOCK
FROM PRODUCTS C
LEFT JOIN (SELECT PRODUCTID, SUM(QTY) AS QTY_SOLD
FROM INVOICEDETAILS
GROUP BY PRODUCTID
) D ON B.PRODUCTID = D.PRODUCTID
LEFT JOIN (SELECT PRODUCTID,SUM(QTYRECEIVE) AS QTY_STOCKS
FROM PURCHASEORDERDETAILS
GROUP BY PRODUCTID
) E ON B.PRODUCTID = E.PRODUCTID

Looking to your query, I think this could be equivalent (or at least I hope it is):
Select
b.ProductID
, c.ProductName
, Case When SUM(a.Qty) IS NULL then 0 else SUM(a.Qty) end as sold
, Case When SUM(b.QtyReceive) IS NULL then 0 else SUM(b.QtyReceive) end as Stock
, Case When SUM(isnull(a.Qty,0 ) - isnull(b.QtyReceive,0)) IS NULL
then 0
else SUM(isnull(a.Qty,0 ) - isnull(b.QtyReceive,0)) end as RemainingStock
from Products c
left join InvoiceDetails a on c.ProductID = a.ProductID
left join PurchaseOrderDetails b on c.ProductID = b.ProductID
Group By b.ProductID,c.ProductName

Related

Postgresql SUM with filter group column

I have this relational data model
product_table
ID
type
configurable_product_id
1
CONFIGURABLE
null
2
SIMPLE
1
3
SIMPLE
1
4
SIMPLE
1
product_source
ID
product_id
quantity
1
2
50
2
3
20
3
4
10
A table that contains the list of products, with two types: configurable and simple. The simple products are linked to a configurable product.
A product_source table that contains the quantities of the different products.
I would like to make a query to retrieve all the quantities of the products, in this way:
If configurable product, then the quantity is the sum of the quantities of the simple products
If simple product, then it is the quantity of the simple product only.
Here is the expected result with the above data:
result
product_id
quantity
1
80
2
50
3
20
4
10
Do you have any idea how to proceed ?
For the moment, I have thought about this type of request, but I don't know how to complete the 'CONFIGURABLE' case
SELECT
pr.id,
pr.type,
pr.configurable_product_id,
CASE
WHEN (pr.type = 'SIMPLE') THEN ps.quantity
WHEN (pr.type = 'CONFIGURABLE') THEN (????)
END AS quantity
FROM public."product" as pr
LEFT JOIN public."product_source" as ps
ON ps.product_id = pr.id
You can use window function and partition for calculating sum of quantity (it was like group by)
demo
SELECT
pr.id AS product_id,
CASE
WHEN (pr.type = 'SIMPLE') THEN ps.quantity
WHEN (pr.type = 'CONFIGURABLE') THEN SUM(ps.quantity) over ()
END AS quantity
FROM public."product" as pr
LEFT JOIN public."product_source" as ps
ON ps.product_id = pr.id
ORDER BY pr.id
Your (...) is:
(
SELECT SUM(ps2.quantity)
FROM product as pr2
LEFT JOIN product_source as ps2
ON ps2.product_id = pr2.id
WHERE pr2.configurable_product_id = pr.id
)

Designing and Querying Product / Review system

I created a product / review system from scratch and I´m having a hard time to do the following query in SQL Server.
My schema has different tables, for: products, reviews, categories, productPhotos and Brand. I have to query them all to find the brand and category name, photos details, Average Rating and Number of Reviews.
I´m having a hard time to get No. of reviews and average rating.
Reviews can be hidden (user has deleted) or blocked (waiting for moderation). My product table doesn't have No. of Reviews or Average Rating columns, so I need to count it on that query, but not counting the blocked and hidden ones (r.bloqueado=0 and r.hidden=0).
I have the query below, but it´s counting the blocked and hidden. If I uncomment the "and r.bloqueado=0 and r.hidden=0" part I get the right counting, but then it doesn't show products that has 0 reviews (something I need!).
select top 20
p.id, p.brand, m.nome, c.name,
count(r.product) AS NoReviews, Avg(r.nota) AS AvgRating,
f.id as cod_foto,f.nome as nome_foto
from
tblBrands AS m
inner join
(tblProducts AS p
left join
tblProductsReviews AS r ON p.id = r.product) ON p.brand = m.id
left join
tblProductsCategorias as c on p.categoria = c.id
left join
(select
id_product, id, nome
from
tblProductsFotos O
where
id = (SELECT min(I.id)
FROM tblProductsFotos I
WHERE I.id_product = O.id_product)) as f on p.id = f.id_product
where
p.bloqueado = 0
//Problem - and r.bloqueado=0 and r.hidden=0
group by
p.id, p.brand, p.modalidade, m.nome, c.name, f.id,f.nome"
Need your advice:
I have seen other systems that has Avg Rating and No. of Reviews in the product table. This would help a lot in the complexity of this query (probably also performance), but then I have to do extra queries in every new review, blocked and hidden actions. I can easily to that. Considering that includes and updates occurs much much less than showing the products, this sounds nice.
Would be a better idea to do that ?
Or is it better to find a way to fix this query ? Can you help me find a solution ?
Thanks
For count the number of product you can use case when and sum assigning 1 there the value is not r.bloqueado=0 or r.hidden=0 and 0 for these values (so you can avoid the filter in where)
"select top 20 p.id, p.brand, m.nome, c.name, sum(
case when r.bloqueado=0 then 0
when r.hidden=0 then 0
else 1
end ) AS NoReviews,
Avg(r.nota) AS AvgRating, f.id as cod_foto,f.nome as nome_foto
from tblBrands AS m
inner join (tblProducts AS p
left join tblProductsReviews AS r ON p.id=r.product ) ON p.brand = m.id
left join tblProductsCategorias as c on p.categoria=c.id
left join (select id_product,id,nome from tblProductsFotos O
where id = (SELECT min(I.id) FROM tblProductsFotos I
WHERE I.id_product = O.id_product)) as f on p.id = f.id_product where p.bloqueado=0
group by p.id, p.brand, p.modalidade, m.nome, c.name, f.id,f.nome"
for avg could be you can do somethings similar
It's very easy to lose records when combining a where clause with an outer join. Rows that do not exist in the outer table are returned as NULL. Your filter has accidentally excluded these nulls.
Here's an example that demonstrates what's happening:
/* Sample data.
* There are two tables: product and review.
* There are two products: 1 & 2.
* Only product 1 has a review.
*/
DECLARE #Product TABLE
(
ProductId INT
)
;
DECLARE #Review TABLE
(
ReviewId INT,
ProductId INT,
Blocked BIT
)
;
INSERT INTO #Product
(
ProductId
)
VALUES
(1),
(2)
;
INSERT INTO #Review
(
ReviewId,
ProductId,
Blocked
)
VALUES
(1, 1, 0)
;
Outer joining the tables, without a where clause, returns:
Query
-- No where.
SELECT
p.ProductId,
r.ReviewId,
r.Blocked
FROM
#Product AS p
LEFT OUTER JOIN #Review AS r ON r.ProductId = p.ProductId
;
Result
ProductId ReviewId Blocked
1 1 0
2 NULL NULL
Filtering for Blocked = 0 would remove the second record, and therefore ProductId 2. Instead:
-- With where.
SELECT
p.ProductId,
r.ReviewId,
r.Blocked
FROM
#Product AS p
LEFT OUTER JOIN #Review AS r ON r.ProductId = p.ProductId
WHERE
r.Blocked = 0
OR r.Blocked IS NULL
;
This query retains the NULL value, and ProductId 2. Your example is a little more complicated because you have two fields.
SELECT
...
WHERE
(
Blocked = 0
AND Hidden = 0
)
OR Blocked IS NULL
;
You do not need to check both fields for NULL, as they appear in the same table.

SQL rewrite to optimize

I'm trying to optimize or change the SQL to work with inner joins rather than independent calls
Database: one invoice can have many payment records and order (products) records
Original:
SELECT
InvoiceNum,
(SELECT SUM(Orders.Cost) FROM Orders WHERE Orders.Invoice = InvoiceNum and Orders.Returned <> 1 GROUP BY Orders.Invoice) as vat_only,
(SELECT SUM(Orders.Vat) FROM Orders WHERE Orders.Invoice = InvoiceNum and Orders.Returned <> 1 GROUP BY Orders.Invoice) as sales_prevat,
(SELECT SUM(pay.Amount) FROM Payments as pay WHERE Invoices.InvoiceNum = pay.InvoiceNum ) as income
FROM
Invoices
WHERE
InvoiceYear = currentyear
I'm sure we can do this another way by grouping and joining tables together. When I tried the SQL statement below, I wasn't getting the same amount (count) of records...I'm thinking in respect to the type of join or where it joins !! but still couldn't get it working after 3 hrs of looking on the screen..
So far I got to...
SELECT
Invoices.InvoiceNum,
Sum(Orders.Cost) AS SumOfCost,
Sum(Orders.VAT) AS SumOfVAT,
SUM(distinct Payments.Amount) as money
FROM
Invoices
LEFT JOIN
Orders ON Orders.Invoice = Invoices.InvoiceNum
LEFT JOIN
Payments ON Invoices.InvoiceNum = Payments.InvoiceNum
WHERE
Invoices.InvoiceYear = 11
AND Orders.Returned <> 1
GROUP BY
Invoices.InvoiceNum
Sorry for the bad english and I'm not sure what to search for to find if it's already been answered here :D
Thanks in advance for all the help
Your problem is that an order has multiple lines for an invoice and it has multiple payments on an invoice (sometimes). This causes a cross product effect for a given order. You fix this by pre-summarizing the tables.
A related problem is that the join will fail if there are no payments, so you need left outer join.
select i.InvoiceNum, osum.cost, osum.vat, p.income
from Invoice i left outer join
(select o.Invoice, sum(o.Cost) as cost, sum(o.vat) as vat
from orders o
where Returned <> 1
group by o.Invoice
) osum
on osum.Invoice = i.InvoiceNum left outer join
(select p.InvoiceNum, sum(pay.Amount) as income
from Payments p
group by p.InvoiceNum
) psum
on psum.InvoiceNum = i.InvoiceNum
where i.InvoiceYear = year(getdate())
Two comments: Is the key field for orders really Invoice or is it also InvoiceNum? Also, do you have a field Invoice.InvoiceYear? Or do you want year(i.InvoiceDate) in the where clause?
Assuming that both payments and orders can contain more than one record per invoice you will need to do your aggregates in a subquery to avoid cross joining:
SELECT Invoices.InvoiceNum, o.Cost, o.VAT, p.Amount
FROM Invoices
LEFT JOIN
( SELECT Invoice, Cost = SUM(Cost), VAT = SUM(VAT)
FROM Orders
WHERE Orders.Returned <> 1
GROUP BY Invoice
) o
ON o.Invoice = Invoices.InvoiceNum
LEFT JOIN
( SELECT InvoiceNum, Amount = SUM(Amount)
FROM Payments
GROUP BY InvoiceNum
) P
ON P.InvoiceNum = Invoices.InvoiceNum
WHERE Invoices.InvoiceYear = 11;
ADDENDUM
To expand on the CROSS JOIN comment, imagine this data for an Invoice (1)
Orders
Invoice Cost VAT
1 15.00 3.00
1 10.00 2.00
Payments
InvoiceNum Amount
1 15.00
1 10.00
When you join these tables as you did:
SELECT Orders.*, Payments.Amount
FROM Invoices
LEFT JOIN Orders
ON Orders.Invoice = Invoices.InvoiceNum
LEFT JOIN Payments
ON Invoices.InvoiceNum = Payments.InvoiceNum;
You end up with:
Orders.Invoice Orders.Cost Orders.Vat Payments.Amount
1 15.00 3.00 15.00
1 10.00 2.00 15.00
1 15.00 3.00 10.00
1 10.00 2.00 10.00
i.e. every combination of payments/orders, so for each invoice you would get many more rows than required, which distorts your totals. So even though the original data had £25 of payments, this doubles to £50 because of the two records in the order table. This is why each table needs to be aggregated individually, using DISTINCT would not work in the case there was more than one payment/order for the same amount on a single invoice.
One final point with regard to optimisation, you should probably index your tables, If you run the query and display the actual execution plan SSMS will suggest indexes for you, but at a guess the following should improve the performance:
CREATE NONCLUSTERED INDEX IX_Orders_InvoiceNum ON Orders (Invoice) INCLUDE(Cost, VAT, Returned);
CREATE NONCLUSTERED INDEX IX_Payments_InvoiceNum ON Payments (InvoiceNum) INCLUDE(Amount);
This should allow both subqueries to only use the index on each table, with no bookmark loopup/clustered index scan required.
Try this, note that I haven't tested it, just wipped it out on notepad. If any of your invoices may not exist in any of the subtables, then use LEFT JOIN
SELECT InvoiceNum, vat_only, sales_prevat, income
FROM Invoices i
INNER JOIN (SELECT Invoice, SUM(Cost) [vat_only], SUM(Vat) [sales_prevat]
FROM Orders
WHERE Returned <> 1
GROUP BY Invoice) o
ON i.InvoiceNum = o.Invoice
INNER JOIN (SELECT SUM(Amount) [income]
FROM Payments) p
ON i.InvoiceNum = p.InvoiceNum
WHERE i.InvoiceYear = currentyear
select
PreQuery.InvoiceNum,
PreQuery.VAT_Only,
PreQuery.Sales_Prevat,
SUM( Pay.Amount ) as Income
from
( select
I.InvoiceNum,
SUM( O.Cost ) as VAT_Only,
SUM( O.Vat ) as sales_prevat
from
Invoice I
Join Orders O
on I.InvoiceNum = O.Invoice
AND O.Returned <> 1
where
I.InvoiceYear = currentYear
group by
I.InvoiceNum ) PreQuery
JOIN Payments Pay
on PreQuery.InvoiceNum = Pay.InvoiceNum
group by
PreQuery.InvoiceNum,
PreQuery.VAT_Only,
PreQuery.Sales_Prevat
Your "currentYear" reference could be parameterized or you can use from getting the current date from sql function such as
Year( GetDate() )

Selecting records in SQL that have the minimum value for that record based on another field

I have a set of data, and while the number of fields and tables it joins with is quite complex, I believe I can distill my problem down using the required fields/tables here for illustration regarding this particular problem.
I have three tables: ClientData, Sources, Prices
Here is what my current query looks like before selecting the minimum value:
select c.RecordID, c.Description, s.Source, p.Price, p.Type, p.Weight
from ClientData c
inner join Sources s ON c.RecordID = s.RecordID
inner join Prices p ON s.SourceID = p.SourceID
This produces the following result:
RecordID Description Source Price Type Weight
=============================================================
001002003 ABC Common Stock Vendor 1 104.5 Close 1
001002003 ABC Common Stock Vendor 1 103 Bid 2
001002003 ABC Common Stock Vendor 2 106 Close 1
001002003 ABC Common Stock Vendor 2 100 Unknwn 0
111222333 DEF Preferred Stk Vendor 3 80 Bid 2
111222333 DEF Preferred Stk Vendor 3 82 Mid 3
111222333 DEF Preferred Stk Vendor 2 81 Ask 4
What I am trying to do is display prices that belong to the same record which have the minimum non-zero weight for that record (so the weight must be greater than 0, but it has to be the minimum from amongst the remaining weights). So in the above example, for record 001002003 I would want to show the close prices from Vendor 1 and Vendor 2 because they both have a weight of 1 (the minimum weight for that record). But for 111222333 I would want to show just the bid price from Vendor 3 because its weight of 2 is the minimum, non-zero for that record. The result that I'm after would like like:
RecordID Description Source Price Type Weight
=============================================================
001002003 ABC Common Stock Vendor 1 104.5 Close 1
001002003 ABC Common Stock Vendor 2 106 Close 1
111222333 DEF Preferred Stk Vendor 3 80 Bid 2
Any ideas on how to achieve this?
EDIT: This is for SQL Server Compact Edition.
I was able to come up with the solution so I thought I would share it:
SELECT x.RecordID, VendorSource, VendorPrice
FROM ClientData x
INNER JOIN Sources s ON x.RecordID = s.RecordID
INNER JOIN Prices p ON s.SourceID = p.SourceID
INNER JOIN (SELECT c.RecordID, MIN(Weight) min_weight
FROM ClientData c
INNER JOIN Sources s ON c.RecordID = s.RecordID
INNER JOIN Prices p ON s.SourceID = p.SourceID
WHERE Weight != 0
GROUP BY c.RecordID) w ON x.RecordID = w.RecordID
WHERE p.Weight = w.min_weight
This allows the minimum weight to be populated on a RecordID level in the derived table, so there is 1 weight per RecordID.
For all those who gave answers, thank you; I appreciate the help and any guidance that was offered.
You can use RANK() with a Partition over RecordId with increasing weights to 'rate' each row (after excluding zero weights entirely), and then simply filter out the top ranked rows. The CTE used just to keep the second query simple + clear
;WITH MyRecords AS
(
-- Your source query goes here
select c.RecordID, c.Description, s.Source, p.Price, p.Type, p.Weight
from ClientData c
inner join Sources s ON c.RecordID = s.RecordID
inner join Prices p ON s.SourceID = p.SourceID
)
SELECT RecordID, [Description], [Source], [Price], [Type], [Weight]
FROM
(
SELECT RecordID, [Description], [Source], [Price], [Type], [Weight],
-- With ranking, the lower the weight the better
Rnk = RANK() OVER (PARTITION BY RecordId ORDER BY [Weight] ASC)
FROM MyRecords
-- But exclude Weight 0 entirely
WHERE [Weight] > 0
) RankedRecords
-- We just want the top ranked records, with ties
WHERE Rnk = 1
Edit CE constraint added after the post. See How would I duplicate the Rank function in a Sql Server Compact Edition SELECT statement? on how to simulate RANK() over in CE.
I think you need to change your structure up a little bit to actually make this work as you would like it to. Basically the way you have it a price record is set up against a Source rather than against the Item which seems to be in the ClientData table. By removing the c.Record number column from the Sources table and putting it into the Prices table you should get the correct One(ClientData) to many (Prices), and One(ClientData) to many(Sources) relationships that I think you need.
select c.RecordID, c.Description, s.Source, p.Price, p.Type, p.Weight
from ClientData c
inner join Prices p ON c.RecordID = p.RecordID
inner join Sources s ON s.SourceID = p.SourceID
AND p.Weight> 0
LEFT OUTER JOIN #Prices p2 ON c.RecordID = p2.RecordID
AND p2.PriceID <> p.priceID
AND p2.Weight > 0
AND p2.Weight < p.Weight
WHERE p2.SourceID IS NULL
If you make the change specified above then this query will returns the exact data that you are looking for.

Pervasive Sql 10 Join one table, onto another, onto another

I have a table with products.
When I get information from that table, I would also like to get the ETA of that article. To do so, I am planning to get the latest purchase Order Row, that is on this article, and then get the expected delivery of this purchase.
This is three different tables and I would like it to be like another column on the query, so I can get the value from the column like I would if it was on the same table.
Is my idea possible? If there is no purchase order on this article I would like the value to be null.
Products
Int ProductId
Int Price
Sample data
ProductId Price
-----------------
1 100
2 300
PORows
Int RowId
Int ProductId
Int POId
Sample data
RowId ProductId POId
-----------------------
1 1 1
PO
Int POId
DateTime ETA
Sample data
POId ETA
-----------------------
1 2010-10-25 10:05
So the result I would want is:
ProductId Price ETA (null if no rows exist)
------------------------------------------------
1 100 2010-10-25 10:05
2 300 NULL
Use:
SELECT p.productid,
p.price,
x.max_eta
FROM PRODUCTS p
LEFT JOIN POROWS r ON r.productid = p.productid
LEFT JOIN (SELECT po.id,
MAX(po.eta) AS max_eta
FROM PO po
GROUP BY po.id) x ON x.poid = r.poid
Pervasive is the only database I'm aware of that won't allow you to omit the INNER and OUTER keywords. v10 might've relaxed that, but I know it's the case for v8 and 2000.
I don't know Pervasive but in SQL standard you can make the select for the latest PO an aliased subquery
select Products.id, products.name, ProductETAS.ETA
from Products
left join
(
select POLINES.productid, min(PO.ETA) as ETA from PO inner join POLINES
on PO.id = POLINES.POid and POLINES.productid = ?
where PO.ETA >= today
group by POLINES.productid
) as ProductETAS
on Products.productid = ProductETAS.productid