SQL Query Add to total sum if.. else dont add - sql

How Do I achieve this result ? What I need is calculate total cost of a Product when a product is made up of components. New for me, I should add a 100$ to total cost if customer chooses for a service called Delivery.
This is what I have tried so far.
Select Sum(Component.Cost*ProductComponent.Quantity) as TotCost from ProductComponent Left Join Component on ProductComponent.ComponentId = Component.ComponentId
I Guess this will get me total cost of a product.
Now There is another table Service which has a many to many relationship with Order. Order has a many to many relationship with Service. What I need is I need to add another 100$ in total cost if there is 'deliverly' used in service.
I have attached an ER diagram of my database structure. I hope my question is clear.

The basic idea is that you'd have to put a case statement in there to add the $100 if there is a record in the Service table for a given order. The below query should get you most of the way there, but looking at the cardinality of your relationships you may need to group the results or use subqueries to chop it down to one row.
SELECT CASE WHEN sa.ServiceID IS NOT NULL THEN SUM(Component.Cost*ProductComponent.Quantity) + 100
ELSE SUM(Component.Cost*ProductComponent.Quantity) END AS TotCost
FROM ProductComponent pc
LEFT JOIN Component on ProductComponent.ComponentId = Component.ComponentId
JOIN OrderLine o ON o.ProductID = pc.ProductID
JOIN StaffAssignment sa ON sa.SaleID = o.SaleID
It looks like multiple StaffAssignments could give Service for one order so that's where you might want to use a subquery like SELECT Top 1 ServiceID FROM StaffAssignment WHERE SaleID = o.SaleID AND ServiceID IS NOT NULL
I don't have time to test this at the moment but hopefully it gives you some ideas to get this solved.

You can see the service as another product and add it to the ones already there with a UNION
SELECT TotCost = SUM(LineCost)
FROM (SELECT c.Cost * pc.Quantity as LineCost
FROM OrderLine ol
INNER JOIN ProductComponent p ON ol.ProductID = pc.ProductID
LEFT JOIN Component c on pc.ComponentId = c.ComponentId
Where ol.SaleID = #ID
UNION ALL
SELECT 100
FROM StaffAssignment sa
INNER JOIN Service s ON sa.ServiceID = s.ServiceID
Where Name = 'Delivery'
And sa.SaleID = #ID) a
Adding a field to the Services table would be useful, if the services have a flat values, to avoid to have magic constants in your code/queries

Related

Getting repeats of output, possibly doing a join wrong?

I'm having an issue where I'm getting some incorrect values in my output. I am binding the below highlighted table column with the circled column down the bottom. The service_id on the highlighted column is what is unique, but I need to bind the booking_id to retrieve the info (if that makes sense. What I end up getting is the top table where I get repeats, or the price is wrong. I should be getting only 5 lines in the top table.
Here's my code. I suspect I might be doing the join wrong?
SELECT bad.agent as Agents,
dog.SUPPLIER as SUPPLIER,
bad.status as TheStatus,
country.analysis_master1 as Country,
ftb.booking_actual_retail as BookingActualRetail,
ftb.Booking_Actual_Cost as BookingCost,
ftb.Booking_Actual_Retail_inc as BookingRetailINC,
fts.Service_Id,
fts.Service_Actual_Retail_inc as ServiceActualCostInc,
Product.SERVICE,
Product.SL_STATUS as SLSTATUS,
cat.name as Product2,
bad.LAST_SERVICE_DATE as Servicedate,
bad.LW_DATE as LWDATE,
ftb.Entered_Date as DATEENTERED,
ftb.Booking_Pax as PEOPLE,
ftb.Booking_Children as KIDS,
bad.TRAVELDATE as TRAVELDATE,
bad.FULL_REFERENCE
from BHD bad
inner join FTB on bad.FULL_REFERENCE = ftb.booking_reference
inner join FTS on FTB.Booking_Id = fts.Booking_Id
inner join DRM Country on bad.agent = country.code
inner join BSL Product on bad.BHD_ID = Product.BHD_ID
inner join SRV cat on Product.SERVICE = cat.CODE
inner join OPT dog on Product.OPT_ID = dog.OPT_ID
where bad.STATUS = 'IV' AND bad.FULL_REFERENCE = 'LTIT129488'
UPDATE:
Ok, so it looks like this join here causes the multiple outputs:
inner join FTS on FTB.Booking_Id = fts.Booking_Id
I have included the two tables, their headers, and sample data
You have somewhere put the join for the service or supplier in the wrong way.. Please check this line again.
inner join SRV cat on Product.SERVICE = cat.CODE
UPDATED SOLUTION :
As per your updated screenshots, I found the issue...
you have joined like this.
inner join FTB on bad.FULL_REFERENCE = ftb.booking_reference
In this join, your one table has single record against booking reference while another have multiple records against booking refrence. Thats why you are getting the multiple records in the output.
Remove this join and your problem will be solved. If you really want the data from this table then you can select in other way like using outer apply etc.

My question is about SQL, using a TOP function inside a sub-query in MS Access

Overall what I'm trying to achieve is a query that shows the most ordered item from a customer in a database. To achieve this I've tried making a query showing how many times a customer has ordered an item, and now I am trying to create a sub-query in it using TOP1 to discern the most bought items.
With the SQL from the first query (looking weird because I made it with the Access automatic creator):
SELECT
Customers.CustomerFirstName,
Customers.CustomerLastName,
Products.ProductName,
COUNT(SalesQuantity.ProductCode) AS CountOfProductCode
FROM (Employees
INNER JOIN (Customers
INNER JOIN Sales
ON Customers.CustomerCode = Sales.CustomerCode)
ON Employees.EmployeeCode = Sales.EmployeeCode)
INNER JOIN (Products
INNER JOIN SalesQuantity
ON Products.ProductCode = SalesQuantity.ProductCode)
ON Sales.SalesCode = SalesQuantity.SalesCode
GROUP BY
Customers.CustomerFirstName,
Customers.CustomerLastName,
Products.ProductName
ORDER BY
COUNT(SalesQuantity.ProductCode) DESC;
I have tried putting in a subquery after FROM line:
(SELECT TOP1 CountOfProduct(s)
FROM (.....)
ORDER by Count(SalesQuantity.ProductCode) DESC)
I'm just not sure what to put in for the "from"-every other tutorial has the data from an already created table, however this is from a query that is being made at the same time. Just messing around I've put "FROM" and then listed every table, as well as
FROM Count(SalesQuantity.ProductCode)
just because I've seen that in the order by from the other code, and assume that the query is discerning from this count. Both tries have ended with an error in the syntax of the "FROM" line.
I'm new to SQL so sorry if it's blatantly obvious, but any help would be greatly appreciated.
Thanks
As I understand, you want the most purchased product for each customer.
So, begin by building aggregate query that counts product purchases by customer (appears to be done in the posted image). Including customer ID in the query would simplify the next step which is to build another query with TOP N nested query.
Part of what complicates this is unique record identifier is lost because of aggregation. Have to use other fields from the aggregate query to provide unique identifier. Consider:
SELECT * FROM Query1 WHERE CustomerID & ProductName IN
(SELECT TOP 1 CustomerID & ProductName FROM Query1 AS Dupe
WHERE Dupe.CustomerID = Query1.CustomerID
ORDER BY Dupe.CustomerID, Dupe.CountOfProductCode DESC);
Overall what I'm trying to achieve is a query that shows the most ordered item from a customer in a database.
This answers your question. It does not modify your query which is only tangentially related.
SELECT s.CustomerCode, sq.ProductCode, SUM(sq.quantity) as qty
FROM Sales as s INNER JOIN
SalesQuantity as sq
ON s.SalesCode = sq.SalesCode
GROUP BY s.CustomerCode, sq.ProductCode;
To get the most ordered items, you can use this twice:
SELECT s.CustomerCode, sq.ProductCode, SUM(sq.quantity) as qty
FROM Sales as s INNER JOIN
SalesQuantity as sq
ON s.SalesCode = sq.SalesCode
GROUP BY s.CustomerCode, sq.ProductCode
HAVING sq.ProductCode IN (SELECT TOP 1 sq2.ProductCode
FROM Sales as s2 INNER JOIN
SalesQuantity as sq2
ON s2.SalesCode = sq2.SalesCode
WHERE s2.CustomerCode = s.CustomerCode
GROUP BY sq2.ProductCode
);
In almost any other database, this would be simpler, because you would be able to use window functions.

SQL Query with 4 Tables but showing multiple rows

Apologies for my basic question, my minds gone blank and for some reason I can't think for the life of me what I've done.
Ok, so I have four tables
dbo.loanunits (this contains all loan units including status of in or out and loanid)
dbo.loanunits2 (this contains extra info on loan units including customer id and loanid)
dbo.loantrans (this contains transactions of loanunits, i.e. history of statuses in's and outs. also includes loanid of item sent out)
dbo.customers (this contains customer id and customer name)
This is my script:
SELECT
dbo.loanunits.loan_id, dbo.loanunits.loan_name,
dbo._loanunits.loan_status, dbo.customers.customername
FROM
dbo.loanunits
INNER JOIN
dbo.loantrans ON dbo.loanunits.loan_id = dbo.loantrans.loan_tranunitid
INNER JOIN
dbo.loanunits2 ON dbo.loantrans.loan_id = dbo.loanunits2.loan_id
INNER JOIN
dbo.customers ON dbo.loanunits2.customer_account_code = dbo.customers.customer_account_code
WHERE
(dbo.loanunits.loan_status = 'out')
I am currently getting the number of results in the transactions table but I should be getting only unique values in the loan units as I only want to see loan units out with customers and who they are with.
I do not see any use of loantrans table, so you may remove it.
SELECT
dbo.loanunits.loan_id, dbo.loanunits.loan_name,
dbo._loanunits.loan_status, dbo.customers.customername
FROM
dbo.loanunits
/*INNER JOIN
dbo.loantrans ON dbo.loanunits.loan_id = dbo.loantrans.loan_tranunitid */
INNER JOIN
/* dbo.loanunits2 ON dbo.loantrans.loan_id = dbo.loanunits2.loan_id */
dbo.loanunits2 ON dbo.loanunits.loan_id = dbo.loanunits2.loan_id
INNER JOIN
dbo.customers ON dbo.loanunits2.customer_account_code = dbo.customers.customer_account_code
WHERE
(dbo.loanunits.loan_status = 'out')
Also, you could easily use the DISTINCT keyword.

SQL notes getting joined twice because they are related to service not unit

Ok I have a database that looks as follows:
Orders, Services, OrderUnits, Notes
Orders: id
Services: id, orderid
Units: id, Orderid, unitnumber
Notes: id, relatedid, text, date
Orders have 1 to many order units
Orders have 1 to many services
Services have 1 to many notes
I am trying to relate only the notes that relate to the unit and show those records, the problem is there is no database relation between notes and unit, only service and note.
I am trying to get the following:
select u.unitnumber
,n.date
,o.id
,s.id
,n.text
FROM tblorders o
INNER JOIN tblServices s on o.id = s.orderid
INNER JOIN tblUnits u on o.id = u.orderid
INNER JOIN tblNotes n on s.id = n.RelatedId
WHERE n.Text LIKE '<p>The status for the Unit # % has changed from % to OUTTOVENDOR</p>'
The problem here is that this gives me the 2 units 4 times if there is 2 units in the same service because the service is related to the note and not the unit.
The note does contain the unit number in it so I was wondering if I could do anything there to somehow relate the note to the unit number?
Thanks!
If at all possible, please rename your columns to be the same in every table. Don't call it just "ID" in the order table, call it OrderID (capitalization optional). Since RelatedID refers to the service table, change it to ServiceID instead, for crying out loud! This will save so much renaming and aliasing and incorrect joins and general all-around confusion. I promise you this.
If Notes are always about Units, they need to be linked to Units relationally through a column. You've simply got to add the column UnitID to the Notes table. Anything else is a horrible, performance-clobbering hack that will come back and haunt you.
Even adding UnitID to the Notes table is still not fully normalized because a Note could refer to a Service and a Unit that aren't for the same order. Would you please explain more about what Units and Services are and how they and Notes all relate to each other? I bet there's a way to fix it.
If notes should always be related to units, your best best is to fix the design not hack it up with something that will be a performance killer. Why can't you add a column for unitid, populate it once using the hack to get existing data and then have the user interface changed to add the unit id when it adds the note?
Incidentally you are using a text field and those are deprecated, you probably should look at changing it to a varchar(max) as soon as possible (this assumes you are on SQL Server 2005 or higher).
Do you need the order units? If you have orders related to services related to notes, the orders -> orderUnits relation will cause your recordset to increase exponentially, like you're seeing, because there isn't a direct relationship there.
Assuming that u.unitnumber is an integer:
select u.unitnumber
,n.date
,o.id
,s.id
,n.text
FROM tblorders o
INNER JOIN tblServices s on o.id = s.orderid
INNER JOIN tblUnits u on o.id = u.orderid
INNER JOIN tblNotes n on s.id = n.RelatedId
AND n.Text LIKE '<p>The status for the Unit # % has changed from % to OUTTOVENDOR</p>'
AND CAST(SUBSTRING(n.Text, 30, CHARINDEX(' ', n.Text, 30) - 30) AS int) = u.unitnumber

SQL Syntax for Complex Scenario (Deals)

i have a complex query to be written but cannot figure it out
here are my tables
Sales --one row for each sale made in the system
SaleProducts --one row for each line in the invoice (similar to OrderDetails in NW)
Deals --a list of possible deals/offers that a sale may be entitled to
DealProducts --a list of quantities of products that must be purchased in order to get a deal
now im trying to make a query which will tell me for each sale which deals he may get
the relevant fields are:
Sales: SaleID (PK)
SaleProducts: SaleID (FK), ProductID (FK)
Deals: DealID (PK)
DealProducts: DealID(FK), ProductID(FK), Mandatories (int) for required qty
i believe that i should be able to use some sort of cross join or outer join, but it aint working
here is one sample (of about 30 things i tried)
SELECT DealProducts.DealID, DealProducts.ProductID, DealProducts.Mandatories,
viwSaleProductCount.SaleID, viwSaleProductCount.ProductCount
FROM DealProducts
LEFT OUTER JOIN viwSaleProductCount
ON DealProducts.ProductID = viwSaleProductCount.ProductID
GROUP BY DealProducts.DealID, DealProducts.ProductID, DealProducts.Mandatories,
viwSaleProductCount.SaleID, viwSaleProductCount.ProductCount
The problem is that it doesn't show any product deals that are not fulfilled (probably because of the ProductID join). i need that also sales that don't have the requirements show up, then I can filter out any SaleID that exists in this query where AmountBought < Mandatories etc
Thank you for your help
I'm not sure how well I follow your question (where does viwSaleProductCount fit in?) but it sounds like you will want an outer join to a subquery that returns a list of deals along with their associated products. I think it would go something like this:
Select *
From Sales s Inner Join SaleProducts sp on s.SaleID = sp.SaleID
Left Join (
Select *
From Deals d Inner Join DealProducts dp on d.DealID = dp.DealId
) as sub on sp.ProductID = sub.ProductID
You may need to add logic to ensure that deals don't appear twice, and of course replace * with the specific column names you'd need in all cases.
edit: if you don't actually need any information from the sale or deal tables, something like this could be used:
Select sp.SaleID, sp.ProductID, sp.ProductCount, dp.DealID, dp.Mandatories
From SaleProducts sp
Left Join DealProducts as dp on sp.ProductID = dp.ProductID
If you need to do grouping/aggregation on this result you will need to be careful to ensure that deals aren't counted multiple times for a given sale (Count Distinct may be appropriate, depending on your grouping). Because it is a Left Join, you don't need to worry about excluding sales that don't have a match in DealProducts.