Background
I have three tables: Stock, PurchaseEntry, and SalesEntry.
These tables have the fields ProductName, MRP, LandinPrice and Qty in common.
I need to join these tables in order to get SUM(Qty) in PurchaseEntry and SalesEntry with the same ProductName, MRP, and LandinPrice as Stock.
Problem
I tried using the following query, but the result was not as I desired.
SUM(Qty) of PurchaseEntry Gives some random results, whereas SUM(Qty) of SalesEntry is correct.
But when I use JOIN on PurchaseEntry alone individually, I get the correct values.
Question
How I can get the proper results, and what type of JOINs should be used?
SQL Code
SELECT Stock.ProductName,
Stock.MRP,
Stock.LandinPrice,
SUM(PurchaseEntry.TotalQty)
AS PurchaseQty,
SUM(SalesEntry.Qty)
AS SalesQty
FROM Stock
JOIN PurchaseEntry
ON Stock.ProductName = PurchaseEntry.ProductName
AND Stock.MRP = PurchaseEntry.MRP
AND Stock.LandinPrice = PurchaseEntry.LandinPrice
JOIN SalesEntry
ON Stock.ProductName = SalesEntry.ProductName
AND Stock.MRP = SalesEntry.MRPPrice
AND Stock.LandinPrice = SalesEntry.LandingAmt
GROUP BY Stock.ProductName,
Stock.MRP,
Stock.LandinPrice
Solved:
I tried changing the Query myself. And I got the proper Output that i required. Below is that Query.
SELECT Stock.ProductName,
Stock.MRP,Stock.LandinPrice,
(SELECT SUM(PurchaseEntry.TotalQty)
FROM PurchaseEntry
WHERE Stock.ProductName=PurchaseEntry.ProductName
AND Stock.MRP=PurchaseEntry.MRP
AND Stock.LandinPrice=PurchaseEntry.LandinPrice) AS PurchaseQty,
(SELECT SUM(SalesEntry.Qty)
FROM SalesEntry
WHERE SalesEntry.ProductName=Stock.ProductName
AND SalesEntry.MRPPrice=Stock.MRP
AND SalesEntry.LandingAmt= Stock.LandinPrice ) AS SalesQty
FROM Stock
GROUP BY Stock.ProductName,Stock.MRP,Stock.LandinPrice
You might try something like
SELECT Stock.ProductName,Stock.MRP,Stock.LandinPrice,
(SELECT SUM(PurchaseEntry.TotalQty)
FROM PurchaseEntry
WHERE PurchaseEntry.ProductName=Stock.ProductName
AND PurchaseEntry.MRP=Stock.MRP
AND PurchaseEntry.LandinPrice=Stock.LandinPrice) AS PurchaseQty
(SELECT SUM(SalesEntry.Qty)
FROM SalesEntry
WHERE SalesEntry.ProductName=Stock.ProductName
AND SalesEntry.MRPPrice=Stock.MRP
AND SalesEntry.LandingAmt=Stock.LandinPrice) AS SalesQty
FROM Stock
Your existing query would have PurchaseQty multiplied by the number of sales and SalesQty multiplied by the number of purchases. Of course I'm assuming that the combination of stock ProductName, MRP, and LandinPrice are unique.
SELECT
Stock.ProductName,
Stock.MRP,
Stock.LandinPrice,
SUM(PurchaseEntry.TotalQty) AS PurchaseQty,
Sales.SalesQty
FROM
Stock
INNER JOIN PurchaseEntry ON Stock.ProductName = PurchaseEntry.ProductName AND Stock.MRP = PurchaseEntry.MRP
AND Stock.LandinPrice = PurchaseEntry.LandinPrice
INNER JOIN
(
SELECT SUM(SE.Qty) AS 'SalesQty', SE.ProductName, SE.LandingAmt, SE.MRPPrice
FROM SalesEntry SE INNER JOIN
Stock S ON S.LandinPrice = SE.LandingAmt AND S.MRP = SE.MRPPrice AND S.ProductName = SE.ProductName
GROUP BY SE.ProductName, SE.LandingAmt, SE.MRPPrice
) Sales ON Sales.ProductName = Stock.ProductName AND Sales.LandingAmt = Stock.LandinPrice
AND Sales.MRPPrice = Stock.MRP
GROUP BY
Stock.ProductName,
Stock.MRP,
Stock.LandinPrice
Related
I'm trying to run a LEFT join in BQ where I'm trying to join in some product classifications to the transactions data, but the revenue figures I end up with are much higher than the actual output. Can anybody see any glaring mistake I've made?
`with transactions as (
select
transaction_date,
retail_unit_code,
order_no,
line_type_code,
item_no,
item_type,
delivery_method,
customer_type,
sales_channel,
created_gross_quantity as created_gross_quantity,
created_sales_gross_amount_euro as created_sales_gross_amount_euro
from `sales.dataset`
),
item_data as (
select
item_no,
item_name,
item_type,
item_classification_1,
item_classification_2,
item_classification_3
from `item.classification.lookup`
group by 1,2,3,4,5,6)
select
t.transaction_date,
t.retail_unit_code,
t.order_no,
t.line_type_code,
t.item_no,
t.item_type,
t.delivery_method,
t.customer_type,
t.sales_channel,
(t.created_gross_quantity) as gross_quantity,
(t.created_sales_gross_amount_euro) as gross_revenue_euro,
id.item_name,
id.item_classification_1,
id.item_classification_2,
id.item_classification_3,
from transactions t
left join item_data id
on t.item_no = id.item_no and t.item_type = id.item_type`
I've tried setting SUM for the metrics as well, but that, funnily enough, doesn't make any difference...
How do I change this query into a nested query?
The query and tables are listed below.
SELECT
Nation.N_NAME as "nation",
ROUND(
SUM(
Lineitem.L_QUANTITY * (Lineitem.L_EXTENDEDPRICE - Lineitem.L_DISCOUNT)
), 2
) AS "order size"
FROM Nation
JOIN Supplier ON Nation.N_NATIONKEY = Supplier.S_NATIONKEY
JOIN Customer ON Supplier.S_NATIONKEY = Customer.C_NATIONKEY
JOIN Orders ON Customer.C_CUSTKEY = Orders.O_CUSTKEY
JOIN Lineitem ON Orders.O_ORDERKEY = Lineitem.L_ORDERKEY
WHERE Lineitem.L_SUPPKEY = Supplier.S_SUPPKEY
GROUP BY Nation.N_NAME
;
tables goes as follows
Nation : N_NATIONKEY, N_NAME
Supplier : S_SUPPKEY, S_NAME, S_NATIONKEY
Customer : C_CUSTKEY, C_NAME, C_NATIONKEY
Orders: O_ORDERKEY, O_CUSTKEY
Lineitem: L_ORDERKEY, L_SUPPKEY, L_QUANTITY, L_EXTENDEDPRICE, L_DISCOUNT
I'm not sure exactly what kind of nested join you're looking for, but here is one option:
SELECT
src.N_NAME as "nation",
ROUND(
SUM(
lineitem.L_QUANTITY * (lineitem.L_EXTENDEDPRICE - lineitem.L_DISCOUNT)
), 2
) AS "order size"
FROM lineitem -- Get line items
INNER JOIN (
SELECT nation.N_NAME, supplier.S_SUPPKEY, Orders.O_ORDERKEY
FROM nation
JOIN Supplier ON Nation.N_NATIONKEY = Supplier.S_NATIONKEY
JOIN Customer ON Supplier.S_NATIONKEY = Customer.C_NATIONKEY
JOIN Orders ON Customer.C_CUSTKEY = Orders.O_CUSTKEY
) src ON lineitem.L_SUPPKEY = src.S_SUPPKEY AND lineitem.L_ORDERKEY = src.O_ORDERKEY
GROUP BY src.N_NAME
I haven't tested it but give it a try and see if it works. If it doesn't give you what you want, please post some sample data.
I am finding it difficult to explain exactly what I am trying to achieve so I think it best to show a visual representation.
Example of how my query results currently look
Example of how I want the results to look
The report I am running shows a list of every product within orders. Each product has its own cost assigned to it. Another column is a delivery charge, but this is a charge assigned to the order; not individual products. I want to be able to show the delivery charge against the first product in each order ONLY.
I have attempted, for far too long, to try and find an answer to this query but have had no luck. I don't know if it is even possible so assistance of any sort, or even just being pointed in the right direction, would be of great help.
Thanks
EDIT.
If it helps here is my query:
SELECT dbo.Orders.EncryptedOrderId,
dbo.OrderProduct.ProductID,
dbo.OrderProduct.QuantityPerRecipient,
dbo.OrderProduct.NumRecipients,
dbo.OrderProduct.TotalQuantity,
dbo.DocType.Name AS [Product Type],
dbo.ProductGroup_Culture.Name AS [Product Group],
RIGHT(CatalogNo, CHARINDEX('_', REVERSE('_' + CatalogNo)) -1) AS [HamptonsType],
FORMAT(dbo.Orders.DateOrderCreated, 'dd/MM/yyyy HH:mm:ss') AS 'DateOrderCreated',
CAST(REPLACE(dbo.Orders.ClearingResult, 'utf-8', 'utf-16') AS XML ).value('(/UserData//CostCenter/node())[1]', 'nvarchar(max)') AS [Cost Center],
dbo.Users.FirstName,
dbo.Users.LastName,
dbo.Users.CompanyName AS [Branch Name],
dbo.Users.Department AS Subsidiary,
dbo.Users.Custom1,
dbo.Users.Custom2,
dbo.Users.Custom3,
dbo.Users.Custom4,
dbo.Users.Custom5,
dbo.OrderProduct.TotalPrice,
dbo.Orders.ShippingCharges,
dbo.OrderProduct.OrderProductID,
dbo.FileSubmissionDocument.OriginalFileType,
COALESCE (dbo.FileSubmissionDocument.Title, dbo.Product_Culture.Name) AS [Product Name],
OPDV.FriendlyValue AS 'BCard Recipient'
FROM dbo.DocType
INNER JOIN dbo.Doc
ON dbo.DocType.DocTypeID = dbo.Doc.DocTypeID
INNER JOIN dbo.OrderProduct
ON dbo.Doc.ProductID = dbo.OrderProduct.ProductID
LEFT JOIN dbo.Product
ON dbo.Product.ProductID = dbo.Doc.ProductID
LEFT JOIN dbo.ProductGroupMembership
ON dbo.ProductGroupMembership.ProductID = dbo.Doc.ProductID
LEFT JOIN dbo.ProductGroup_Culture
ON dbo.ProductGroup_Culture.ProductGroupID = dbo.ProductGroupMembership.ProductGroupID
INNER JOIN dbo.Orders
ON dbo.OrderProduct.OrderID = dbo.Orders.OrderID
INNER JOIN dbo.Users
ON dbo.Orders.UserID = dbo.Users.UserID
INNER JOIN dbo.Product_Culture
ON dbo.OrderProduct.ProductID = dbo.Product_Culture.ProductID
INNER JOIN dbo.Store_Culture
ON dbo.Store_Culture.StoreID = dbo.Users.AssignedToStoreID FULL OUTER
JOIN dbo.FileSubmissionDocument
ON dbo.OrderProduct.OrderProductID = dbo.FileSubmissionDocument.SubOrderProductID - 1
LEFT JOIN (SELECT OP.OrderProductID,
OP.DialID,
OP.FriendlyValue
FROM OrderProductDialValue OP
LEFT JOIN Dial DI ON DI.DialID = OP.DialID
LEFT JOIN OrderProduct OT ON OT.OrderProductID = OP.OrderProductID
LEFT JOIN Product PR ON PR.ProductID = OT.ProductID
WHERE PR.ExternalID = 'BCName'
AND DI.UProduceDialName = 'Name') OPDV ON OPDV.OrderProductID = dbo.OrderProduct.OrderProductID
WHERE ('#CUSTOMERNAME' is null
OR '#CUSTOMERNAME' = ''
OR dbo.Store_Culture.Name LIKE '%' + '#CUSTOMERNAME' + '%')
AND dbo.OrderProduct.IsDraft = 0
AND dbo.Orders.IsCart=0
AND dbo.Orders.IsSaveForLater=0
AND (('#DATE' <= dbo.Orders.DateOrderCreated)
OR ('#DATE' IS NULL)
OR ('#DATE'=''))
AND ((DATEADD(day, 1, '#DATE') >= dbo.Orders.DateOrderCreated)
OR ('#DATE' IS NULL)
OR ('#DATE'=''))
AND dbo.Users.LastName NOT LIKE '%TEST%'
ORDER BY dbo.Orders.OrderID DESC, dbo.OrderProduct.OrderProductID DESC
The query runs through a reporting system on an online portal so the values that show as #CUSTOMERNAME or #DATE are variables based on values given at the time when the report is run.
this may help you
select orderid,
productid,
productvalue,
case ROW_NUMBER() over (partition by orderid order by orderid)
when 1 then deliverycharge
else null end as 'deliverycharge'
from ........
I assume your query looks like
select orderID
, productID
, productValue
, DeliveryCharge
from test_t
order by orderID
, productValue desc
;
and that you want delivery charges listed for the most expensive product of each order.
If supported by your rdbms, you can use the analytic RANK function
select orderID
, productID
, productValue
, DeliveryCharge
, CASE RANK() OVER ( PARTITION BY orderID ORDER BY productValue DESC ) WHEN 1 THEN DeliveryCharge ELSE NULL END r
from test_t
order by orderID
, productValue desc
;
If your rdbms does not support RANK, you can emulate it using a left join with a suitably aggregated copy of your table:
select t.orderID
, t.productID
, t.productValue
, rt.mdc DeliveryCharge
from test_t t
left join (
select orderID
, max(productValue) mp
, max(DeliveryCharge) mdc
from test_t
group by orderID
) rt
on (
rt.orderID = t.orderID
AND rt.mp = t.productValue
)
order by orderID
, productValue desc
;
The tacit assumption is that the delivery charge is by order (which seems reasonable as you wouldn't want to selectively drop it otherwise, right ?).
Moreover, both solutions will produce multiple rows containing the delivery charge per order if that order contains multipleproducts with the same productValue.
Tested on oracle 12c1;
I was developed a pos software with sqlite database.It was fined in early but now it is too slow with many data storage.Some of select view query take at least 3 minutes for only read in c# data reader.It is so bad.It may be wrong in database table structure.The worst performance views is following:
Firstly i have Stocks Table,Storage Storage Table,Purchase Receive Items Table and Sale Voucher Items.
Stock table has 3000 rows.Stock Storage table has 4600 rows. Purchase Receive Items table has 4500 rows and sale voucher items table has 130,00 rows.I was created view for show sum of purchase item and sum of sale items like that=>
select
Sto.stockcode,
Sto.StockName,
IfNull((select sum(ss.OpeningQty) from StockStorages ss
where ss.stockid=Sto.stockid and ss.status='A'),0) [OpeningQty],
IfNull((select sum(pur.qty) from purchasereceiveitems pur
where pur.stockid=Sto.stockid and pur.status='A'),0) [Purqty],
IfNull((select sum(svi.qty) from salevoucheritems svi
where svi.stockid=Sto.stockid and svi.status='A'),0) [Salqty],
IfNull((select sum(sti.qty) from StockTransactionItems sti
where sti.stockid=Sto.stockid and sti.status='A'),0) [Transactionqty],
IfNull((select sum(ss.CurrentQty) from StockStorages ss
where ss.stockid=Sto.stockid and ss.status='A'),0) [CurrentQty]
from stocks Sto
where Sto.status='A'
group by Sto.StockCode
How can i improve my structure or my code.Please advice me.
Turn your correlated subqueries (O(n^2) performance) into left joins like this:
select
s.stockcode,
s.stockname,
ifnull(a.opening_qty, 0) opening_qty,
ifnull(b.Purqty, 0) Purqty,
ifnull(c.salqty, 0) salqty,
ifnull(d.Transactionqty, 0) Transactionqty,
ifnull(e.CurrentQty, 0) CurrentQty
from StockCode s
left join (select
stockid,
sum(OpeningQty) opening_qty
from StockStorages
where status='A'
group by stockid) a on s.stockid = a.stockid
left join (select
stockid,
sum(qty) Purqty
from purchasereceiveitems
where status='A'
group by stockid) b on s.stockid = b.stockid
(select
stockid,
sum(qty) salqty
from salevoucheritems
where status='A'
group by stockid) c on s.stockid = c.stockid
(select
stockid,
sum(qty) Transactionqty
from StockTransactionItems
where status='A'
group by stockid) d on s.stockid = d.stockid
(select
stockid,
sum(CurrentQty) CurrentQty
from StockStorages
where status='A'
group by stockid) e on s.stockid = e.stockid
where s.status = 'A';
This query can be sped up by indexing the columns that are used for lookups, so that the subqueries no longer need to search the entire table for each single value:
CREATE INDEX StockStorages_ss ON StockStorages (stockid, status);
CREATE INDEX purchasereceiveitems_ss ON purchasereceiveitems (stockid, status);
CREATE INDEX salevoucheritems_ss ON salevoucheritems (stockid, status);
CREATE INDEX StockTransactionItems_ss ON StockTransactionItems(stockid, status);
CREATE INDEX StockStorages_ss ON StockStorages (stockid, status);
I have this query, I have an Acquisitions table (Incoming) and Invoice Table (Outgoing) I am trying to calculate the Value on Hand by taking the AVG dbo.tblAcqDetail.AcqPrice * the QtyOnHand which is figured taking Incoming - Outgoing. When I add a line item on the Acquisitions table that has a different cost for the same item, the AVG is not grouping and showing instead two line items example below. The shipment side works fine with multiple line items...
Product QtyIn QtyOut On_Hand AVGPrice Value_OnHand
Screws 100 30 70 25.0000 1750.0000
Nuts 50 10 40 40.0000 1600.0000
Nuts 100 10 90 50.0000 4500.0000
Bolts 100 20 80 100.000 8000.0000
.
SELECT DISTINCT
dbo.tblProduct.Product ,
SUM(DISTINCT dbo.tblAcqDetail.AcqQuantity) AS QtyIN ,
SUM(DISTINCT dbo.tblInvoiceDetail.InvQuantity) AS QtyOut ,
SUM(DISTINCT dbo.tblAcqDetail.AcqQuantity)
- SUM(DISTINCT dbo.tblInvoiceDetail.InvQuantity) AS On_Hand ,
dbo.tblAcqDetail.AcqPrice ,
dbo.tblAcqDetail.AcqPrice
* ( SUM(DISTINCT dbo.tblAcqDetail.AcqQuantity)
- SUM(DISTINCT dbo.tblInvoiceDetail.InvQuantity) ) AS Value_Hand
FROM dbo.tblAcq
INNER JOIN dbo.tblAcqDetail ON dbo.tblAcq.acqID = dbo.tblAcqDetail.AcqID
INNER JOIN dbo.tblProduct ON dbo.tblAcqDetail.ProductID = dbo.tblProduct.ProductID
INNER JOIN dbo.tblInvoiceDetail ON dbo.tblProduct.ProductID = dbo.tblInvoiceDetail.ProductID
INNER JOIN dbo.tblInvoice ON dbo.tblInvoiceDetail.InvoiceID = dbo.tblInvoice.InvoiceID
GROUP BY dbo.tblProduct.Product ,
dbo.tblAcqDetail.AcqPrice
Basing on PinnyM's answer, you don't need DISTINCT, I re-write your query as following using table alias:
SELECT
P.Product ,
SUM( AcD.AcqQuantity) AS QtyIN ,
SUM( InD.InvQuantity) AS QtyOut ,
SUM( AcD.AcqQuantity)
- SUM( InD.InvQuantity) AS On_Hand ,
AcD.AcqPrice ,
AcD.AcqPrice
* ( SUM(AcD.AcqQuantity)
- SUM( InD.InvQuantity) ) AS Value_Hand
FROM dbo.tblAcq Ac
INNER JOIN dbo.tblAcqDetail AcD ON Ac.acqID = AcD.AcqID
INNER JOIN dbo.tblProduct P ON AcD.ProductID = P.ProductID
INNER JOIN dbo.tblInvoiceDetail InD ON P.ProductID = InD.ProductID
INNER JOIN dbo.tblInvoice Inv ON InD.InvoiceID = Inv.InvoiceID
GROUP BY P.Product ,
AcD.AcqPrice
By reading this query, I don't understand why you need table dbo.tblInvoice, it is not part of aggregation.
The reason you still see different product is because you group by two columns P.Product, AcD.AcqPrice, not only Product, in your return result you can see their combination is unique.
To be mathematically accurate, you should not be using SUM(DISTINCT fieldname), but just SUM(fieldname). Otherwise, it will eliminate entries that happen to have the same quantity.
For that matter, you shouldn't be using DISTINCT at the beginning of your query either, GROUP BY already handles that.
If you believe you have duplicate rows being returned by your JOINs (which you shouldn't really if you're doing it right), wrap them in a subquery using DISTINCT before trying to aggregate.
As an example, a subquery to eliminate duplicates can be written like so:
SELECT
Product ,
SUM(AcqQuantity) AS QtyIN ,
SUM(InvQuantity) AS QtyOut ,
SUM(AcqQuantity)
- SUM(InvQuantity) AS On_Hand ,
AcqPrice ,
AcqPrice
* ( SUM(AcqQuantity)
- SUM(InvQuantity) ) AS Value_Hand
FROM (SELECT DISTINCT
dbo.tblProduct.Product ,
dbo.tblAcqDetail.AcqQuantity,
dbo.tblInvoiceDetail.InvQuantity,
dbo.tblAcqDetail.AcqPrice
FROM
dbo.tblAcqDetail
INNER JOIN dbo.tblProduct ON dbo.tblAcqDetail.ProductID = dbo.tblProduct.ProductID
INNER JOIN dbo.tblInvoiceDetail ON dbo.tblProduct.ProductID = dbo.tblInvoiceDetail.ProductID
INNER JOIN dbo.tblInvoice ON dbo.tblInvoiceDetail.InvoiceID = dbo.tblInvoice.InvoiceID ) productInfo
GROUP BY Product, AcqPrice