Select distinct values from one column and sum another column - sql-server

I have this stored procedure shown here which calculates the order qty, rejection and percentage for rejection against each department, but I have an issue: for summing the order qty, I need to consider only distinct order number to sum the order qty, but for rejection qty all rows should be considered. How to achieve this?
Stored procedure:
CREATE PROCEDURE orderqtyper
#fromDate Date,
#toDate Date
AS
SELECT
Department,
SUM(Order_Qty) AS [Order Qty],
SUM(Rejection_Qty) AS [Rejection Qty],
FORMAT((SUM(Rejection_Qty) * 100.0 / NULLIF(SUM(Order_Qty), 0) / 100), 'P') AS Percentage
FROM
Semicon_NPD
WHERE
(Date BETWEEN #fromDate AND #toDate)
GROUP BY
Department
ORDER BY
Percentage DESC
Sample table:
Current result:
Expected result (if the the order number is the same, it should take sum of only unique values for order qty but sum of all for rejections qty):

You can use DISTINCT as SUM(DISTINCT <column_name>)
create proc orderqtyper
#fromDate Date,
#toDate Date
as
SELECT Department,
SUM(DISTINCT Order_Qty)as [Order Qty],
SUM(Rejection_Qty) as [Rejection Qty],
Format((SUM(Rejection_Qty) * 100.0 / NULLIF(SUM(Order_Qty), 0)/100),'P') AS Percentage
FROM Semicon_NPD
Where (Date between #fromDate and #toDate)
GROUP BY Department
order by Percentage desc
go

Related

Group By Month and Year to get distinct month and years then combine as a single date SQL

SELECT CONVERT(DATE,CAST([Year] AS VARCHAR(4))+'-'+
CAST([Month] AS VARCHAR(2))+'-'+
CAST('1' AS VARCHAR(2))) Date
FROM (SELECT YEAR(Date) as [Year], MONTH(Date) as [Month] FROM [dbo].[Data]
GROUP BY YEAR(Date), MONTH(Date)) x
ORDER BY Date DESC
Is there a better way to doinq this with a single query?
The query should return the unique month and year from a table but as combined Date.
IF OBJECT_ID ('tempdb..#TempT') IS NOT NULL DROP TABLE #TempT
CREATE TABLE #TempT(
dt datetime)
INSERT INTO #TempT (dt) VALUES
('2016-10-11'),
('2016-10-3'),
('2016-9-13'),
('2016-9-16')
SELECT DISTINCT CAST(DATEADD(month, DATEDIFF(month, 0, dt), 0) as DATE) AS Dates
from #TempT
Sample data is really a necessity for these kinds of questions, but this function could also be a help for you DATEFROMPARTS
SELECT
DATEFROMPARTS([Year], [Month], 01)
FROM
(
SELECT
YEAR(Date) as [Year],
MONTH(Date) as [Month]
FROM [dbo].[Data]
GROUP BY YEAR(Date), MONTH(Date)
) x
ORDER BY Date DESC
This code takes whatever date you enter and evaluates it as the first of the month. Effectively looking at only month and year:
SELECT
DATEADD(MM,DATEDIFF(MM,0, [Date] ),0) AS [YearMonth]
FROM [dbo].[Data]
ORDER BY
DATEADD(MM,DATEDIFF(MM,0, [Date] ),0)
;

SQL Sum over not bounded by where clauses

I am using the SUM Over the first time and have the following query noe:
SELECT Id, Amount, Date, TotalAmount = SUM(Amount) OVER (order by Amount)
FROM Account
WHERE Date >= '2016-03-01' AND Date <= '2016-03-10' AND UserId = 'xyz'
ORDER BY ValutaDate
The TotalAmount should be a running total over the whole table for a specific user (so the Sum Over clause should respect the where clause for the user). On the other hand I just need a few records and not the whole table, thats why I added the where clause specifying the date range. But now, of course, my sum gets calculated just for the range I specified.
What should I do, to get just a few records specified by date range but get the sum calculated over the whole table though. Is there an performant way to accomplish this?
Thanks in advance for helping me out.
Break the running total into its own query
; WITH all_rows_one_user as (SELECT *
, TotalAmount = SUM(Amount) OVER (order by ValutaDate)
FROM Account
WHERE UserId = 'xyz')
SELECT Id, Amount, Date, TotalAmount
FROM all_rows_one_user
WHERE Date >= '2016-03-01' AND Date <= '2016-03-10'
ORDER BY ValutaDate
Same query, different syntax:
SELECT Id, Amount, Date, TotalAmount
FROM (SELECT *
, TotalAmount = SUM(Amount) OVER (order by ValutaDate)
FROM Account
WHERE UserId = 'xyz') AS all_rows_one_user
WHERE Date >= '2016-03-01' AND Date <= '2016-03-10'
ORDER BY ValutaDate
The WHERE clause is applied first so the SUM can't access rows that don't match that.
You can use apply though. Note it will be reading the entire table for that user so might not perform too well without a decent index.
SELECT a.Id, a.Amount, a.Date, ta.TotalAmount
FROM Account a
OUTER APPLY (SELECT SUM(CASE WHEN a2.Date <= Account.Date THEN a2.TotalAmount ELSE 0 END) AS TotalAmount FROM Account a2 WHERE a2.UserId = Account.UserId) ta
WHERE a.Date >= '2016-03-01' AND a.Date <= '2016-03-10' AND a.UserId = 'xyz'

SQL SUM filter query

We have a table with the following information:
table1
I need T-SQL code that receives a "start date" and "end date and would generate the invoice total, grouped by customer id and invoice type, of all invoices generated within the date range AND the total invoiced for that customer (credit and cash combined).
For instance, the result if we provide start date 01-10-2012 and end date 10-11-2012 should be:
result_table
This is what I have:
DECLARE #startdate DATE, #enddate DATE
SET #startdate = '01-10-2012'
SET #enddate = '10-11-2012'
SELECT CustomerId, InvoiceType, SUM(Total) As Total
FROM Invoices
WHERE Date BETWEEN #startdate AND #enddate
GROUP BY CustomerID, InvoiceType
It works fine, but I am unable to come up with a way to calculate the "total2" column, since I'm already grouping rows by "invoicetype".
Help, please.
Thank you.
This should work:
SELECT CustomerId, InvoiceType, SUM(Total) As Total
, (
SELECT SUM(Total) FROM Invoices t2 WHERE t2.CustomerId=t1.CustomerId
) AS Total2
FROM Invoices t1
WHERE Date BETWEEN #startdate AND #enddate
GROUP BY CustomerID, InvoiceType

Calculating total cost per month sql

I want to work out the total cost for a specific month, my code is generating the items purchased that month but it is calculating the cost of all the items instead of just that month
set dateformat 'dmy'
Select OrderID, OrderDate, UnitPrice
From SupplierOrder
Where OrderDate between '01/07/2015' AND '31/07/2015'
group by OrderID, OrderDate, UnitPrice
SELECT SUM(UnitPrice) AS TotalCostOfItems FROM SupplierOrder;
I'm a little confused as to why you have two queries. Regardless, the first is filtering on date, but the second is not - hence the second query is calculating the summed unit price for all orders. To get the second query working, simply add the same filter that you have for the first
SELECT SUM(UnitPrice) AS TotalCostOfItems FROM SupplierOrder Where OrderDate between '01/07/2015' AND '31/07/2015';
you should add the where statement after your select.
SELECT SUM(UnitPrice) AS TotalCostOfItems FROM SupplierOrder
Where OrderDate between '01/07/2015' AND '31/07/2015'
hope this help
Try this
select mnth, yr, sum(unitprice) unitprice
from
(
SELECT
DATEPART(month,orderdate) mnth,
datepart(year,orderdate) yr,
unitprice
from supplierorder
) a
group by mnth, yr

SQL Server - Get customers with nth order in specific date range

I'm tasked with the following:
Select a list of all customers who had their nth order during a certain date range (usually a specific month).
This list needs to contain: customer id, sum of first n orders
My tables are something like this:
[dbo.customers]: customerID
[dbo.orders]: orderID, customerID,
orderDate, orderTotal
Here is what I've tried so far:
-- Let's assume our threshold (n) is 10
-- Let's assume our date range is April 2013
-- Get customers that already had n orders before the beginning of the given date range.
DECLARE #tmpcustomers TABLE (tmpcustomerID varchar(8))
INSERT INTO
#tmpcustomers
SELECT
c.customerID
FROM
orders o
INNER JOIN customers c ON o.customerID = c.customerID
WHERE
o.orderDate < '2013-04-01'
GROUP BY c.customerID
HAVING (COUNT(o.orderID) >= 10)
-- Now get all customers that have n orders sometime within the given date range
-- but did not have n orders before the beginning of the given date range.
SELECT
a.customerID, SUM(orderTotal) AS firstTenOrderTotal
SELECT
o.customerID, o.orderID, o.orderTotal
FROM
orders o
INNER JOIN customers c ON c.customerID = o.customerID
WHERE
a.customerID NOT IN ( SELECT tmpcustomerID FROM #tmpcustomers )
AND
o.orderDate > '2013-04-01'
AND
o.orderDate < '2013-05-01'
GROUP BY c.customerID
HAVING COUNT(o.orderID) >= 10
This seems to work but it's clunky and slow. Another big problem is that the firstTenOrderTotal is actually the SUM of the total amount of orders by the end of the given date range and not necessarily the first 10.
Any suggestions for a better approach would be much appreciated.
In the insert to #tmpcustomers, why are you joining back to the customer table? The order table already has the customerID that you want. Also, why are you looking for orders where the order date is before your date range? Don't you just want customers with more than n orders between a date range? This will make the second query easier.
By only having the customers with n or more orders in the table variable #tmpcustomers, you should just be able to join it and the orders table in the second query to get the sum of all the orders for those customers where you would once again limit order table records to your date range (so you do not get orders outside of that range). This will remove the having statement and the join to the customers table in your final result query.
Give this a try. Depending on your order distribution it may perform better. In this query im assembling the list of orders in the range, and then looking back to count the number of prior orders (also grabbing the orderTotal).
note: I am assuming the orderID increments as orders are placed.
If this isnt the case just use a row_number over the date to project the sequence into the query.
declare #orders table (orderID int primary key identity(1,1), customerID int, orderDate datetime, orderTotal int)
insert into #orders (customerID, orderDate, orderTotal)
select 1, '2013-01-01', 1 union all
select 1, '2013-01-02', 2 union all
select 1, '2013-02-01', 3 union all
select 2, '2013-01-25', 5 union all
select 2, '2013-01-26', 5 union all
select 2, '2013-02-02', 10 union all
select 2, '2013-02-02', 10 union all
select 2, '2013-02-04', 20
declare #N int, #StartDate datetime, #EndDate datetime
select #N = 3,
#StartDate = '2013-02-01',
#EndDate = '2013-02-20'
select o.customerID,
[total] = o.orderTotal + p.total --the nth order + total prior
from #orders o
cross
apply ( select count(*)+1, sum(orderTotal)
from #orders
where customerId = o.customerID and
orderID < o.orderID and
orderDate <= o.orderDate
) p(n, total)
where orderDate between #StartDate and #EndDate and p.n = #N
Here is my suggestion:
Use Northwind
GO
select ords.OrderID , ords.OrderDate , '<-->' as Sep1 , derived1.* from
dbo.Orders ords
join
(
select CustomerID, OrderID, ROW_NUMBER() OVER(PARTITION BY CustomerID ORDER BY OrderId DESC) AS ThisCustomerCardinalOrderNumber from dbo.Orders
) as derived1
on ords.OrderID = derived1.OrderID
where
derived1.ThisCustomerCardinalOrderNumber = 3
and ords.OrderDate between '06/01/1997' and '07/01/1997'
EDIT:::::::::
I took my CTE example, and reworked it for multiple Customers (seen below).
Give it the college try.
Use Northwind
GO
declare #BeginDate datetime
declare #EndDate datetime
select #BeginDate = '01/01/1900'
select #EndDate = '12/31/2010'
;
WITH
MyCTE /* http://technet.microsoft.com/en-us/library/ms175972.aspx */
( ShipName,ShipAddress,ShipCity,ShipRegion,ShipPostalCode,ShipCountry,CustomerID,CustomerName,[Address],
City,Region,PostalCode,Country,Salesperson,OrderID,OrderDate,RequiredDate,ShippedDate,ShipperName,
ProductID,ProductName,UnitPrice,Quantity,Discount,ExtendedPrice,Freight,ROWID) AS
(
SELECT
ShipName ,ShipAddress,ShipCity,ShipRegion,ShipPostalCode,ShipCountry,CustomerID,CustomerName,[Address]
,City ,Region,PostalCode,Country,Salesperson,OrderID,OrderDate,RequiredDate,ShippedDate,ShipperName
,ProductID ,ProductName,UnitPrice,Quantity,Discount,ExtendedPrice,Freight
, ROW_NUMBER() OVER (PARTITION BY CustomerID ORDER BY OrderDate , ProductName ASC ) as ROWID /* Note that the ORDER BY (here) is directly related to the ORDER BY (near the very end of the query) */
FROM
dbo.Invoices inv /* “Invoices” is a VIEW, FYI */
where
(inv.OrderDate between #BeginDate and #EndDate)
)
SELECT
/*
ShipName,ShipAddress,ShipCity,ShipRegion,ShipPostalCode,ShipCountry,CustomerID,CustomerName,[Address],
City,Region,PostalCode,Country,Salesperson,OrderID,OrderDate,RequiredDate,ShippedDate,ShipperName,
ProductID,ProductName,UnitPrice,Quantity,Discount,ExtendedPrice,Freight,
*/
/*trim the list down a little for the final output */
CustomerID ,OrderID , OrderDate, (ExtendedPrice + Freight) as ComputedTotal
/*The below line is the “trick”. I reference the above CTE, but only get data that is less than or equal to the row that I am on (outerAlias.ROWID)*/
, (Select SUM (ExtendedPrice + Freight) from MyCTE innerAlias where innerAlias.ROWID <= outerAlias.ROWID and innerAlias.CustomerID = outerAlias.CustomerID) as RunningTotal
, ROWID as ROWID_SHOWN_FOR_KICKS , OrderDate as OrderDate
FROM
MyCTE outerAlias
GROUP BY CustomerID ,OrderID, OrderDate, ProductName,(ExtendedPrice + Freight) ,ROWID,OrderDate
/*Two Order By Options*/
ORDER BY outerAlias.CustomerID , outerAlias.OrderDate , ProductName
/* << Whatever the ORDER BY is here, should match the “ROW_NUMBER() OVER ( ORDER BY ________ ASC )” statement inside the CTE */
/*ORDER BY outerAlias.ROWID */ /* << Or, to keep is more “trim”, ORDER BY the ROWID, which will of course be the same as the “ROW_NUMBER() OVER ( ORDER BY” inside the CTE */

Resources