selecting distinct values from master table and repeated values from joined tables - sql

I'm trying to select distinct values from master table and repeated values from child tables.
I have 4 tables:
invoice
invoiceLine
product
businessPartner
My query:
select
c_invoice.c_invoice_ID,
c_bpartner.name as "Business Partner",
M_Product.name as "Product",
c_invoiceline.priceentered as "amount"
from adempiere.c_invoice
left join adempiere.c_invoiceline on c_invoice.c_invoice_ID=c_invoiceline.c_invoice_ID
left join adempiere.M_Product on c_invoiceline.M_Product_ID =M_Product.M_Product_ID
left join adempiere.C_BPartner on c_invoice.c_bpartner_ID=c_bpartner.c_bpartner_id
where c_invoice.sh_booking_ID=1000019 and c_invoice.c_doctypetarget_id=1000005
My query result:
INVOICEID BUSINESS Partner PRODUCT AMT
1000005; "Tehmoor"; "Charge 1"; 1200
1000005; "Tehmoor"; "Standard"; 1500
1000006; "Rafay"; "Charge 1"; 1200
1000006; "Rafay"; "Standard"; 1100
and expected result
INVOICEID BUSINESS Partner PRODUCT AMT
1000005; "Tehmoor"; "Charge 1"; 1200
; NULL; "Standard"; 1500
1000006; "Rafay"; "Charge 1"; 1200
; NULL; "Standard"; 1100

You can try something like this:
select
CASE WHEN row_number() OVER (PARTITION BY mast.id) = 1 THEN
mast.title
ELSE NULL END as title,
joined.measure
from mast
left join joined on (mast_id = mast.id)
I've created a fiddle for it, so you can examine my example schema.
I think it's better to handle this kind of requirement in a host language, because in SQL it's a bit tricky.

I have reproduced your schema in my local environment. Below is the query using which you can achieve your desired result.
SELECT
CASE WHEN (Rank() Over(ORDER BY i.c_invoice_id ASC)) = (Row_Number() Over (ORDER BY i.c_invoice_id ASC)) THEN pt.b_name ELSE NULL END AS "Business Partner",
CASE WHEN (Rank() Over(ORDER BY i.c_invoice_id ASC)) = (Row_Number() Over (ORDER BY i.c_invoice_id ASC)) THEN i.c_invoice_id ELSE NULL END AS Invoice_Id,
pr.b_name,
il.price
FROM invoice i
LEFT JOIN c_invoice_line il ON il.c_invoice_id = il.c_invoice_id
LEFT JOIN c_product pr ON il.product_line_id = pr.b_prod_id
LEFT JOIN c_bpartner pt ON pt.b_partner_id = Trim(il.c_prod_id);
If required, change your table and column names accordingly.

Related

How to get Odoo Inventory adjustment value through SQL

I am working on a custom stock valuation module and in one model I am trying to get adjustment value for a lot - product - warehouse wise of the previous day.
QUERY 1
SELECT COUNT(*)
FROM
(
SELECT stock_inventory.date AS stock_adjustment_date,
stock_move_line.lot_id,
stock_move_line.product_id,
SUM(stock_move_line.qty_done) total_stock_adjustment
FROM stock_move_line
LEFT JOIN stock_move ON stock_move_line.move_id = stock_move.id
LEFT JOIN stock_inventory ON stock_move.inventory_id = stock_inventory.id
WHERE stock_move.inventory_id IS NOT NULL
AND stock_move_line.location_id = 5
AND stock_move_line.location_dest_id = 13
AND stock_move_line.lot_id IS NOT NULL
GROUP BY stock_move_line.lot_id, stock_move_line.product_id, stock_inventory.date
ORDER BY total_stock_adjustment DESC
)
testTable;
QUERY 2
SELECT COUNT(*)
FROM
(
SELECT stock_inventory.date AS stock_adjustment_date,
stock_move_line.lot_id,
stock_move_line.product_id,
SUM(stock_move_line.qty_done) total_stock_adjustment
FROM stock_move_line
LEFT JOIN stock_move ON stock_move_line.move_id = stock_move.id
LEFT JOIN stock_inventory ON stock_move.inventory_id = stock_inventory.id
WHERE stock_move.inventory_id IS NOT NULL
AND stock_move_line.location_id = 13
AND stock_move_line.location_dest_id = 5
AND stock_move_line.lot_id IS NOT NULL
GROUP BY stock_move_line.lot_id, stock_move_line.product_id, stock_inventory.date
ORDER BY total_stock_adjustment DESC
)
testTable;
Why these both queries returning same count 14,849 ?
13 is the warehouse ID and 5 is the virtual location used for adjustment. What I am doing wrong here?

Exclude those orders which has service code 'AAAA'

An order can have multiple services. As per the functionality, I have to remove those orders from the selection which have a service code 'AAAA'.
I mean if an order has two services code 'AAAA' and 'AAAB'. Then this must not be included in the output because this order has a Service code 'AAAA'. Please see the screenshot below for more info.
Query:
Select BO.OrderID ,OrderDate ,BackgroundServiceID , BST.BST_Name ,BST_Code from
BackgroundOrder BO
JOIN BackgroundOrderService BOS ON BO.OrderID = BOS.OrderID
JOIN ams.lkpBkgSvcType BST ON BST.BST_ID = BOS.BackgroundServiceID
Expected Output Query:
Select BO.OrderID ,OrderDate ,BackgroundServiceID , BST.BST_Name ,BST_Code from
BackgroundOrder BO
JOIN BackgroundOrderService BOS ON BO.OrderID = BOS.OrderID
JOIN ams.lkpBkgSvcType BST ON BST.BST_ID = BOS.BackgroundServiceID
Where BST.BST_Code = 'AAAB' AND BO.OrderID not in
(Select BO.OrderID from
BackgroundOrder BO
JOIN BackgroundOrderService BOS ON BO.OrderID = BOS.OrderID
JOIN ams.lkpBkgSvcType BST ON BST.BST_ID = BOS.BackgroundServiceID
Where BST.BST_Code = 'AAAA'
)
Please suggest some other ways to get the above output without using a not-in statement.
You can wrap your query in a cte and count the occurences of 'AAAA' per OrderId then filter where it's zero
with t as (
select BO.OrderID, OrderDate, BackgroundServiceID, BST.BST_Name, BST_Code,
Sum(case when bst_code='AAAA' then 1 else 0 end) over(partition by BO.OrderID) cnt=0
from BackgroundOrder BO
join BackgroundOrderService BOS on BOS.OrderID = BO.OrderID
join ams.lkpBkgSvcType BST on BST.BST_ID = BOS.BackgroundServiceID
)
select OrderID, OrderDate, BackgroundServiceID, BST_Name, BST_Code
from t
where cnt=0

Subquery returned more than 1 value - MS SQL

select Id,Prayaseeid, name,Gender,
(select name from tb_Category where id = Category) AS Category,
ideadescription,Domain,ProjectTerms,ProjectStartDate,Amountsanctioned,
(select Amount from tb_innovatorDisbursement where tbid ='TBINO1111A' and
applyingforcycle='1') AS AmountDisbursed,
projectstatus,projectoutcome
from tb_innovator
where tbid='TBINO1111A 'and applyingforcycle='1'
Use TOP to Limit rows to 1,
The Select queries in parenthesis resulting in more than one record
select Id,Prayaseeid, name,Gender,
(select TOP(1) name from tb_Category where id = Category) AS Category,
ideadescription,Domain,ProjectTerms,ProjectStartDate,Amountsanctioned,
(select TOP(1) Amount
from tb_innovatorDisbursement where tbid ='TBINO1111A' and applyingforcycle='1') AS AmountDisbursed,
projectstatus,projectoutcome from tb_innovator
where tbid='TBINO1111A 'and applyingforcycle='1'
You need to test your sub queries for the where clause to make sure the values you are searching do not have multiple records. I would use (Top 1 ) for any column other than the Id column assuming the Id column must be unique as the name suggests.
Update query
select ti.Id,
ti.Prayaseeid,
ti.name,
ti.Gender,
(select TOP(1) name from tb_Category where id = ti.Category) AS Category,
ti.ideadescription,
ti.Domain,
ti.ProjectTerms,
ti.ProjectStartDate,
ti.Amountsanctioned,
tid.Amount AS AmountDisbursed,
ti.projectstatus,
ti.projectoutcome
from tb_innovator ti
INNER JOIN tb_innovatorDisbursement tid ON tid.Tbid = ti.tbid
AND tid.applyingforcycle='1'
where ti.tbid='TBINO1111A 'and ti.applyingforcycle='1'
Perhaps you should use JOIN:
select i.Id, i.Prayaseeid, i.name, i.Gender, c.Category,
i.ideadescription, i.Domain, i.ProjectTerms, i.ProjectStartDate, i.Amountsanctioned,
id.Amount from tb_innovatorDisbursement
i.projectstatus, i.projectoutcome
from tb_innovator i left join
tb_Category c
on i.category = c.id left join
tb_innovatorDisbursement id
on id.tbid = i.dbid and id.applyingforcycle = '1'
where i.tbid = 'TBINO1111A 'and i.applyingforcycle = '1';
Note that all column names are qualified as well, indicating what table they come from.

SQL - Only show results for one column in first instance of a duplicated record

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;

Trying to Calculate Qty on Hand and Value on hand with multiple shipments

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