How could I combine these 3 T-SQL statements into one? - sql

I have 3 queries that I want to combine. 1 query is for total sales, 1 is for canceled orders, and 1 is for orders that don't include specific product types. Just need the total sales $$ to output in a table format as they are now. The only thing that changes between the 3 is the where statement. Thanks!
Edit: I realize I said "Just need the total sales $$" ... what I meant was I just need the sales $$ for each query in one table. So $x, $y, $z ... x is the total sales, y is the sales dollars that got cancelled, and z is the sales dollars for the specific items.
SELECT Sum((Items.Total+Items.Shipping)*OrderDetails.Quantity) AS Total
FROM Promo INNER JOIN (Orders INNER JOIN (Items INNER JOIN OrderDetails ON Items.ItemCode = OrderDetails.ItemCode) ON Orders.OrderNumber = OrderDetails.OrderNumber) ON Promo.Promo = Orders.Promo
WHERE ((Promo.OfferType)='Sale') AND ((Items.Date) Between '6/1/2010' And '12/31/2011');
SELECT Sum((Items.Total+Items.Shipping)*OrderDetails.Quantity) AS Canceled
FROM Promo INNER JOIN (Orders INNER JOIN (Items INNER JOIN OrderDetails ON Items.ItemCode = OrderDetails.ItemCode) ON Orders.OrderNumber = OrderDetails.OrderNumber) ON Promo.Promo = Orders.Promo
WHERE (((Promo.OfferType)='Sale') AND ((Items.Date) Between '6/1/2010' And '12/31/2011') AND ((Items.Status)="Canceled"));
SELECT Sum((Items.Total+Items.Shipping)*OrderDetails.Quantity) AS BadItems
FROM Promo INNER JOIN (Orders INNER JOIN (Items INNER JOIN OrderDetails ON Items.ItemCode = OrderDetails.ItemCode) ON Orders.OrderNumber = OrderDetails.OrderNumber) ON Promo.Promo = Orders.Promo
WHERE (((Promo.OfferType)='Sale') AND ((Items.Date) Between '6/1/2010' And '12/31/2011') AND ((Items.ProductType)<>2) AND ((Items.ProductType)<>6));
Thanks!

If you want the results on 1 row:
SELECT Total, Canceled, BadItems
FROM Query1, Query2, Query3
And if you want the results in 1 column, use a UNION:
Query1
UNION
Query2
UNION
Query3

Updated to reflect question clarification:
SELECT Sum(CASE WHEN Items.Status <> 'Canceled' AND Items.ProductType = 4 THEN (Items.Total+Items.Shipping)*OrderDetails.Quantity ELSE 0 END) AS Total ,
Sum(CASE WHEN Items.Status = 'Canceled' AND Items.ProductType = 4 THEN (Items.Total+Items.Shipping)*OrderDetails.Quantity ELSE 0 END) AS Canceled,
Sum(CASE WHEN Items.ProductType <> 4 THEN (Items.Total+Items.Shipping)*OrderDetails.Quantity ELSE 0 END) AS BadItems,
FROM Promo INNER JOIN (Orders INNER JOIN (Items INNER JOIN OrderDetails ON Items.ItemCode = OrderDetails.ItemCode) ON Orders.OrderNumber = OrderDetails.OrderNumber) ON Promo.Promo = Orders.Promo
WHERE ((Promo.OfferType)='Sale') AND ((Items.Date) Between '9/1/2010' And '12/31/2010');

try to use a procedure which will accept three values as input which will suite your were statements and it will give results in one table as want. if you are stuck let me know , i will help.

Related

TSQL: Join tables based on criteria

The resultset I am aiming to receive is this:
My table structure is like:
The INVOICE table has INVTYPE column which defines whether the invoice is a sales invoice or a purchase invoice by holding 101 for sale and 201 for purchase.
My query will use a where statement to also filter down the STOCK table to a particular STOCKCODE.
I am trying to filter out products (STOCK) with a STOCKCODE = "XYZ"
If I run
SELECT *
FROM STOCK
WHERE STOCKCODE = 'XYZ'
I get 152 results. So I have 152 products with a STOCKCODE of 'XYZ' for sure..
My problem is when I try to join these tables. For simplicity, I have only tried to get INVTYPE 101 to keep it simple but my desired outcome is still with the 201.
I have tried joins and subqueries such as:
SELECT
S.STOCKNO, S.STOCKNAME, ISNULL(SUM(IIT.QTYSOLD), 0) AS QTY,
ISNULL(SUM(IIT.TOTAL), 0) AS TOTAL
FROM
STOCK S
LEFT JOIN
INVOICE_ITEM IIT ON S.STOCKID = IIT.STOCKID
LEFT JOIN
INVOICE INV ON IIT.INVID = INV.INVID
WHERE
S.STOCKCODE = 'XYZ' AND INV.TRANSTYPE = 101
GROUP BY
S.STOCKNO, S.STOCKNAME
This query returns 122 results. So I know that there are 30 items in the stock table that have not been sold, which I need to show them in the resultset with 0 (zero) in the relevant columns.
I also tried this:
SELECT
S.STOCKNO, S.STOCKNAME,
(SELECT ISNULL(SUM(TOTAL), 0)
FROM INVOICE_ITEM IIT
WHERE IIT.STOCKID = S.STOCKID) AS TOTAL,
(SELECT ISNULL(SUM(QTYSOLD), 0)
FROM INVOICE_ITEM IIT
WHERE IIT.STOCKID = S.STOCKID) AS QTY
FROM
STOCK S
WHERE
S.STOCKCODE = 'XYZ'
This query return the full 152 results because I did not filter by INVTYPE it returned the items which were purchased as well.
I am using SQL Server 2014
So my question is how can I achieve my desired resultset? and what am I doing wrong with my joins?
Thanks
You need a conditional aggregation
SELECT S.STOCKNO,
S.STOCKNAME,
ISNULL(SUM(CASE WHEN INV.TRANSTYPE = 201 THEN IIT.TOTAL END), 0) AS TOTAL_PURCHASED,
ISNULL(SUM(CASE WHEN INV.TRANSTYPE = 101 THEN IIT.TOTAL END), 0) AS TOTAL_SOLD,
ISNULL(SUM(CASE WHEN INV.TRANSTYPE = 201 THEN IIT.QTYSOLD END),0) AS QTY_PURCHASED,
ISNULL(SUM(CASE WHEN INV.TRANSTYPE = 101 THEN IIT.QTYSOLD END),0) AS QTY_SOLD
FROM STOCK S
LEFT JOIN INVOICE_ITEM IIT ON S.STOCKID = IIT.STOCKID
LEFT JOIN INVOICE INV ON IIT.INVID= INV.INVID
WHERE S.STOCKCODE= 'XYZ'
GROUP BY S.STOCKNO, S.STOCKNAME
since you are not joining your inner tables (INVOICE_ITEM) with their primary key this is an expected behavior.
you either need to use INVITEMID column for your join, you need to add STOCKID and INVID together.
SELECT ISNULL(SUM(TOTAL),0) FROM INVOICE_ITEM IIT
WHERE IIT.STOCKID = S.STOCKID and IIT.INVID = INV.INVID
For performance, it is tempting to try:
select s.stockno, s.stockname,
coalesce(i.total_purchased, 0) as total_purchased,
coalesce(i.total_sold, 0) as total_sold,
coalesce(i.qty_purchased, 0) as qty_purchased,
coalesce(i.qty_sold, 0) as qty_sold
from stock s outer apply
(select sum(case when i.transtype = 201 then ii.total end) as total_purchased,
sum(case when i.transtype = 101 then ii.total end) as total_sold,
sum(case when i.transtype = 201 then ii.qtysold end) as qty_purchased,
sum(case when i.transtype = 101 then ii.qtysold end) as qty_sold
from invoice_item ii join
invoice i
on ii.invid = i.invid
where s.stockid = ii.stockid
) i
where stockcode = 'XYZ'

SQL - Tracking Monthly Sales

I am writing a query to summarize sales by month. My problem is that my query only returns records for months with sales. For example, I am looking over a 15 month range. But for one specific part in the example below only 3 of the 15 months had sales.
I'm hoping to have 15 records show up and the other ones have 0's for sales. The reason I am hoping for the additional records is I want to take the standard deviation of this, and dropping records impacts that calculation.
Sample Code:
SELECT I.PartNumber as PartNumber,
YEAR(O.CreateDate) as CreateDateYear,
MONTH(O.CreateDate) as CreateDateMonth,
COUNT(*) as TotalDemand
FROM OrderDetails OD
INNER JOIN Orders O on O.Id = OD.OrderId
INNER JOIN Items I on I.Id = OD.ItemId
WHERE
O.CreateDate >= '1-1-2016'
AND O.CreateDate <= '3-31-2017'
AND I.PartNumber = '5144831-2'
GROUP BY I.PartNumber, YEAR(O.CreateDate) , MONTH(O.CreateDate);
Sample Current Output:
Part # | Year | Month | Demand
5144831-2 2017 1 1
5144831-2 2017 2 3
5144831-2 2016 3 1
Desired Output:
I would want an additional row such as:
5144831-2 2016 11 0
To show there were no sales in Nov 2016.
I do have a temp table #_date_array2 with the possible months/years, I think I need help incorporating a LEFT JOIN.
If you want to use left join, you would not be able to use it directly with the inner join. You can do the inner join inside the parenthesis and then do the left join outside to avoid messing with the results of left join. Try this:
SELECT Z.PartNumber as PartNumber,
YEAR(O.CreateDate) as CreateDateYear,
MONTH(O.CreateDate) as CreateDateMonth,
COUNT(Z.OrderId) as TotalDemand
FROM Orders O
LEFT JOIN
(
SELECT OrderId, PartNumber
FROM
OrderDetails OD
INNER JOIN Items I ON I.Id = OD.ItemId
AND I.PartNumber = '5144831-2'
) Z
ON O.Id = Z.OrderId
AND O.CreateDate >= '1-1-2016'
AND O.CreateDate <= '3-31-2017'
GROUP BY Z.PartNumber, YEAR(O.CreateDate) , MONTH(O.CreateDate);
To get a count of 0 for months with no order, avoid using count(*) and use count(OrderId) as given above.
Note - You will have to make sure the Orders table has all months and years available i.e. if there is no CreateDate value of, say, November 2016 in the Orders table(left table in the join), the output will also not produce this month's entry.
Edit:
Can you try this:
SELECT Z.PartNumber as PartNumber,
YEAR(O.CreateDate) as CreateDateYear,
MONTH(O.CreateDate) as CreateDateMonth,
COUNT(O.OrderId) as TotalDemand
FROM Orders O
RIGHT JOIN
(
SELECT OrderId, PartNumber
FROM
OrderDetails OD
INNER JOIN Items I ON I.Id = OD.ItemId
AND I.PartNumber = '5144831-2'
) Z
ON O.Id = Z.OrderId
AND O.CreateDate >= '1-1-2016'
AND O.CreateDate <= '3-31-2017'
GROUP BY Z.PartNumber, YEAR(O.CreateDate) , MONTH(O.CreateDate);
Assuming you have sales of something in every month, the simplest solution is to switch to conditional aggregation:
SELECT '5144831-2' as PartNumber,
YEAR(O.CreateDate) as CreateDateYear,
MONTH(O.CreateDate) as CreateDateMonth,
SUM(CASE WHEN I.PartNumber = '5144831-2' THEN 1 ELSE 0 END) as TotalDemand
FROM OrderDetails OD INNER JOIN
Orders O
ON O.Id = OD.OrderId INNER JOIN
Items I
ON I.Id = OD.ItemId
WHERE O.CreateDate >= '2016-01-01' AND
O.CreateDate <= '2017-03-31'
GROUP BY YEAR(O.CreateDate) , MONTH(O.CreateDate);
Note: This is something of a hack for solving the problem. More robust solutions involve generating the dates and using LEFT JOIN (or similar functionality). However, this is often the fastest way to get the result.
based on all of your comments on other posts etc it seems like you have a table that has a date range you want and you want to be able to run the analysis for multiple/all of the part numbers. So the main issue is you will need a cartesian join between your date table and partnumbers that were sold during that time in order to accomplish you "0"s when not sold.
;WITH cteMaxMinDates AS (
SELECT
MinDate = MIN(DATEFROMPARTS(CreateDateYear,CreateDateMonth,1))
,MaxDate = MAX(DATEFROMPARTS(CreateDateYear,CreateDateMonth,1))
FROM
#_date_array2
)
;WITH cteOrderDetails AS (
SELECT
d.CreateDateYear
,d.CreateDateMonth
,I.PartNumber
FROM
#_date_array2 d
INNER JOIN Orders o
ON d.CreateDateMonth = MONTH(o.CreateDate)
AND d.CreateDateYear = YEAR(o.CreateDate)
INNER JOIN OrderDetails od
ON o.Id = od.OrderId
INNER JOIN Items i
ON od.ItemId = i.Id
AND i.PartNumber = '5144831-2'
)
, cteDistinctParts AS (
SELECT DISTINCT PartNumber
FROM
cteOrderDetails
)
SELECT
d.CreateDateYear
,d.CreateDateMonth
,I.PartNumber
,COUNT(od.PartNumber) as TotalDemand
FROM
#_date_array2 d
CROSS JOIN cteDistinctParts p
LEFT JOIN cteOrderDetails od
ON d.CreateDateYear = od.CreateDateYear
AND d.CreateDateMonth = od.CreateDateMonth
AND p.PartNumber = od.PartNumber
GROUP BY
d.CreateDateYear
,d.CreateDateMonth
,I.PartNumber
To get ALL part numbers simply remove AND i.PartNumber = '5144831-2' join condition.

How to apply WHERE clause to multiple SELECT statements in SQL Server

I am creating an query that selects data from multiple tables. I have completed all the query but now I have to apply the WHERE clause to the whole query.
I have 9 select statements, and these are working fine. Data is being selected from different tables. Now I want to declare date session and I want all data to be filtered according to the date provided. I am using the below query:
SELECT
(SELECT COUNT(DISTINCT OrderItems.ProductID)
FROM OrderItems) AS 'TotalSoldItemsDistinct',
(SELECT COUNT(OrderItems.ProductID)
FROM OrderItems) AS 'TotalSoldItemsInDistinct',
(SELECT COUNT(Orders.OrderID)
FROM Orders) AS 'TotalOrders',
(SELECT COUNT(Orders.OrderID)
FROM Orders
WHERE Orders.OrderStatusID = #CompleteOStatusID) AS 'CompleteOrders',
(SELECT COUNT(Orders.OrderID)
FROM Orders
WHERE Orders.OrderStatusID = #PendingOStatusID) AS 'PendingOrders',
(SELECT COUNT(Orders.ClientID)
FROM Orders
WHERE Orders.ClientID != #WalkingCustID) AS 'namedcustomers',
(SELECT COUNT(Orders.ClientID)
FROM Orders
WHERE Orders.ClientID = #WalkingCustID) AS 'WalkingCustomers',
(SELECT SUM(OrderItems.PurchasePrice)
FROM OrderItems) AS 'TotalPurchasePrice',
(SELECT SUM(OrderItems.SalePrice)
FROM OrderItems) AS 'TotalSalePrice'
I am selecting data from 2 tables named 'Orders' and 'OrderItems', I have column TransactionDate in 'Orders' table and column OrderDate in OrderItems table on that I want to use where filter. Can anybody please suggest how to apply filter to whole query?
You could try this
;with tempOrderItems AS
(
SELECT
COUNT(DISTINCT OrderItems.ProductID) AS 'TotalSoldItemsDistinct',
COUNT(OrderItems.ProductID) AS 'TotalSoldItemsInDistinct',
SUM(OrderItems.PurchasePrice) AS 'TotalPurchasePrice',
SUM(OrderItems.SalePrice) AS 'TotalSalePrice'
FROM OrderItems ori
WHERE OrderDate BETWEEN xxx AND yyy
)
, tempOrders AS
(
SELECT
COUNT(o.OrderID) AS 'TotalOrders',
SUM(CASE WHEN o.OrderStatusID = #CompleteOStatusID THEN 1 ELSE 0 END) AS 'CompleteOrders',
SUM(CASE WHEN o.OrderStatusID = #PendingOStatusID THEN 1 ELSE 0 END) AS 'PendingOrders',
SUM(CASE WHEN o.ClientID != #WalkingCustID THEN 1 ELSE 0 END) AS 'namedcustomers',
SUM(CASE WHEN o.ClientID = #WalkingCustID THEN 1 ELSE 0 END) AS 'WalkingCustomers'
FROM Orders o
WHERE TransactionDate BETWEEN xxx AND yyy
)
SELECT * FROM tempOrderItems
CROSS JOIN tempOrders
It is not fully clear what you want as a result, but here 2 approaches.
Try the following for selecting data from 2 tables at the same time (replace the date with your criteria):
SELECT * FROM Orders AS o INNER JOIN OrderItems AS i WHERE o.TransactionDate = '2015-02-12' AND i.OrderDate = '2015-02-12';
The SELECT * selects all columns from both tables as a result and the WHERE ... AND ...-clause filters for results only with your defined date.
Try the following for selecting order item data only for Data that matches the date on a specific order.
SELECT i.* FROM Orders AS o INNER JOIN OrderItems AS i WHERE o.TransactionDate = i.OrderDate AND o.OrderID = '12345';
The SELECT i.* tells the query to only return the columns of the OrderItems. And the WHERE o.TransactionDate = i.OrderDate ensures that only order items from the same date of the order with the OrderID "12345" are returned (which is defined with the AND o.OrderID = '12345'. This would work given you have a field "OrderID" on your Order table and you want to use it as a criteria.

SQL Select Query into another query

I am a beginner with SQL so apologise in advance if my terminology / coding is a little off, or maybe way off.
I have two queries which I would like to join into one. The first creating a list of productids which contain two specific processes.
I then want to use these productids in the second query.
Also is below correct?
group by products.productid having (sum(case when processid like 'pick%' then 1 else 0 end) + sum(case when processid like 'pack%' then 1 else 0 end) = 2)
Any help would be much appreciated, hope this makes sense.
SELECT
Products.ProductID
FROM Products
INNER JOIN Categories ON Products.Category = Categories.Category
INNER JOIN Boms ON Products.Product = Boms.Product AND Boms.BomVersion = Products.BomVersion
LEFT OUTER JOIN Products AS Comp ON Boms.Component = Comp.Product
LEFT OUTER JOIN Processes ON Boms.Process = Processes.Process
WHERE
products.active = 1
and Categorys.Categoryid in ('5','20')
group by products.productid
having (sum(case when processid like 'pick%' then 1 else 0 end) + sum(case when processid like 'pack%' then 1 else 0 end) = 2)
order by products.productid
SELECT
Products.ProductID,
Products.productdescription,
Boms.Type As Type,
Comp.ProductId as Component,
Comp.productdescription,
Boms.Quantity,
BomVersions.BomVersionID,
Processes.processid,
Processes.ProcessDescription
FROM Products
INNER JOIN Categories ON Products.Category = Categories.Category
INNER JOIN Boms ON Products.Product = Boms.Product AND Boms.BomVersion = Products.BomVersion
LEFT OUTER JOIN Products AS Comp ON Boms.Component = Comp.Product
LEFT OUTER JOIN Processes ON Boms.Process = Processes.Process
INNER JOIN BomVersions ON Products.BomVersion = BomVersions.BomVersion
WHERE
products.active = 1
order by products.productid, products.type,comp.productid
To combine them you could do the following.
SELECT b.*
FROM
(SELECT Products.ProductID FROM Products INNER JOIN ...) AS a
INNER JOIN
(SELECT Products.ProductID, Products.productdescription, Boms.Type As Type, ...) AS b
ON a.ProductID = b.ProductID

how to check if a type of row exists in a table

Hi I have an order table and a payment table which are linked by order_num. I would like to get entries from order table who have entries in the payment table of PaymentType 'A' as well as PaymentType 'B' and their amounts match. Example order number 411 should only be returned to me if it has at least two payments and one of them is paymenttype 'A' and the other one is paymenttype 'b' and amount for both is 45
Thanks.
Like the comments state, there is very little info to go off of for a truly definitive answer. Here goes a possible solution, at least in a basic form.
SELECT *
FROM Orders o
LEFT JOIN Payment p1 ON o.order_num = p1.order_num
LEFT JOIN Payment p2 ON o.order_num = p2.order_num
WHERE p1.Type = "A"
AND p2.Type = "B"
AND p1.Amount = p2.Amount
As long as you can guarantee that there are at most one A and one B row:
SELECT
<columns here>
FROM
Orders O
INNER JOIN Payments PA ON
PA.order_number = O.order_number AND
PA.payment_type = 'A'
INNER JOIN Payments PB ON
PB.order_number = O.order_number AND
PB.payment_type = 'B'
WHERE
PA.amount = PB.amount
How about this:
SELECT o.OrderID,
o.Amount
FROM Order AS o
INNER JOIN Payment AS pA ON pA.OrderID = o.OrderID AND pA.PaymentType = 'A'
INNER JOIN Payment AS pB ON pB.OrderID = o.OrderID AND pB.PaymentType = 'B'
WHERE pA.Amount = pB.Amount
GROUP BY o.OrderID,
o.Amount
Making some (hopefully) reasonable assumptions about the names of tables, columns, etc, how about...
SELECT *
FROM ORDER_TABLE
WHERE EXISTS (SELECT *
FROM PAYMENT_TABLE
WHERE PAYMENT_TYPE = 'A' AND
PAYMENT_TABLE.ORDER = ORDER_TABLE.ORDER AND
PAYMENT_TABLE.AMOUNT = ORDER_TABLE.AMOUNT) AND
EXISTS (SELECT *
FROM PAYMENT_TABLE
WHERE PAYMENT_TYPE = 'B' AND
PAYMENT_TABLE.ORDER = ORDER_TABLE.ORDER AND
PAYMENT_TABLE.AMOUNT = ORDER_TABLE.AMOUNT);
Assumes amounts exist only in the payment table, and that the payment table may have multiple records for a payment type.
select [columns]
from ORDER O
where exists (select null from PAYMENT PA, PAYMENT PB
where PA.PAYMENT_TYPE = 'A'
and PB.PAYMENT_TYPE = 'B'
and PA.ORDER_NUM = O.ORDER_NUM
and PB.ORDER_NUM = O.ORDER_NUM
and PA.AMOUNT = PB.AMOUNT)