Select row that has max total value SQL Server - sql

I have the following scheme (2 tables):
Customer (Id, Name) and
Sale (Id, CustomerId, Date, Sum)
How to select the following data ?
1) Best customer of all time (Customer, which has Max Total value in the Sum column)
For example, I have 2 tables (Customers and Sales respectively):
id CustomerName
---|--------------
1 | First
2 | Second
3 | Third
id CustomerId datetime Sum
---|----------|------------|-----
1 | 1 | 04/06/2013 | 50
2 | 2 | 04/06/2013 | 60
3 | 3 | 04/07/2013 | 30
4 | 1 | 03/07/2013 | 50
5 | 1 | 03/08/2013 | 50
6 | 2 | 03/08/2013 | 30
7 | 3 | 24/09/2013 | 20
Desired result:
CustomerName TotalSum
------------|--------
First | 150
2) Best customer of each month in the current year (the same as previous but for each month in the current year)
Thanks.

Try this for the best customer of all times
SELECT Top 1 WITH TIES c.CustomerName, SUM(s.SUM) AS TotalSum
FROM Customer c JOIN Sales s ON s.CustomerId = c.CustomerId
GROUP BY c.CustomerId, c.CustomerName
ORDER BY SUM(s.SUM) DESC

One option is to use RANK() combined with the SUM aggregate. This will get you the overall values.
select customername, sumtotal
from (
select c.customername,
sum(s.sum) sumtotal,
rank() over (order by sum(s.sum) desc) rnk
from customer c
join sales s on c.id = s.customerid
group by c.id, c.customername
) t
where rnk = 1
SQL Fiddle Demo
Grouping this by month and year should be trivial at that point.

Related

Count occurence of specific code per customer in 6 month period

I have a table that contains the following:
customerid | date (dmy) | productid
John | 1-3-14 | A
John | 7-5-14 | Y
John | 8-5-14 | Y
John | 1-10-15 | B
John | 1-11-15 | Y
Pete | 1-7-15 | Y
I need to find out how often customer X has bought Product Y in a six-month period.
The start of a period is defined as the first time a customer has bought one of the products A,B, C or Y. The endtime of a period is exactly six months after that.
The next period starts when the customer buys again one of the products A,B,C or Y.
So the output should be
customerid | period-start | period-end | countofY
John | 1-3-14 | 8-5-14 | 2
John | 1-10-15 | 1-11-15 | 1
Pete | 1-7-15 | 1-7-15 | 1
SELECT c.Customerid, MIN(c.pdate) AS startperiod, c1.endperiod,
(
SELECT COUNT(temp.productid) FROM Customer temp
WHERE temp.Customerid = c.Customerid
AND temp.pdate >= MIN(c.pdate)
AND temp.pdate <= c1.endperiod
GROUP BY temp.productid HAVING temp.productid ='Y'
)AS countOfY
FROM Customer c
CROSS APPLY
(
SELECT TOP 1 c1.pdate AS endperiod
FROM Customer c1
WHERE c1.Customerid = c.Customerid
AND c1.pdate >= c.pdate
AND
(
DATEDIFF(MONTH, c.pdate, c1.pdate) < 6
OR
(
SELECT TOP 1 t.pdate FROM Customer t
WHERE t.Customerid = c.Customerid
AND t.pdate < c1.pdate
) IS NULL
)
ORDER BY c1.pdate DESC
)AS c1 GROUP BY c1.endperiod, c.Customerid
;WITH CTE_DateRanges AS (
SELECT
customerid,
productid,
MIN(purchase_date) AS period_start,
DATEADD(MM, 6, MIN(purchase_date)) AS period_end
FROM
My_Table
GROUP BY
customerid,
productid
)
SELECT
DR.customerid,
DR.productid,
DR.period_start,
DR.period_end,
COUNT(*)
FROM
CTE_DateRanges DR
INNER JOIN My_Table MT ON
MT.customerid = DR.customerid AND
MT.productid = DR.productid AND
MT.purchase_date BETWEEN DR.period_start AND DR.period_end
GROUP BY
DR.customerid,
DR.productid,
DR.period_start,
DR.period_end,

SUM in multi-currency

I am trying to do SUM() in a multi-currency setup. The following will demonstrate the problem that I am facing:-
Customer
-------------------------
Id | Name
1 | Mr. A
2 | Mr. B
3 | Mr. C
4 | Mr. D
-------------------------
Item
-------------------------
Id | Name | Cost | Currency
1 | Item 1 | 5 | USD
2 | Item 2 | 2 | EUR
3 | Item 3 | 10 | GBP
4 | Item 4 | 5 | GBP
5 | Item 5 | 50 | AUD
6 | Item 6 | 20 | USD
7 | Item 3 | 10 | EUR
-------------------------
Order
-------------------------
User_Id | Product_Id
1 | 1
2 | 1
1 | 2
3 | 3
1 | 5
1 | 7
1 | 5
2 | 6
3 | 4
4 | 2
-------------------------
Now, I want the output of a SELECT query that lists the Customer Name and the total amount worth of products purchased as:-
Customer Name | Amount
Mr. A | Multiple-currencies
Mr. B | 25 USD
Mr. C | 15 GBP
Mr. D | 2 EUR
So basically, I am looking for a way to add the cost of multiple products under the same customer, if all of them have the same currency, else simply show 'multiple-currencies'. Running the following query will not help:-
SELECT Customer.Name, SUM(Item.Amount) FROM Customer
INNER JOIN Order ON Order.User_Id = Customer.Id
INNER JOIN Item ON Item.Id = Order.Product_Id
GROUP BY Customer.Name
What should my query be? I am using Sqlite
I would suggest two output columns, one for the currency and one for the amount:
SELECT c.Name,
(case when max(currency) = min(currency) then sum(amount)
end) as amount,
(case when max(currency) = min(currency) then max(currency)
else 'Multiple Currencies'
end) as currency
FROM Customer c INNER JOIN
Order o
ON o.User_Id = c.Id INNER JOIN
Item
ON i.Id = o.Product_Id
GROUP BY c.Name
If you want, you can concatenate these into a single string column. I just prefer to have the information in two different columns for something like this.
The above is standard SQL.
I think your query should looks like this
SELECT
Data.Name AS [Customer Name],
CASE WHEN Data.Count > 1 THEN "Multiple-currencies" ELSE CAST(Data.Amount AS NVARCHAR) END AS Amount
FROM
(SELECT
Customer.Name,
COUNT(Item.Currency) AS Count,
SUM(Item.Amount) AS Amount
FROM
Customer
INNER JOIN Order ON Order.User_Id = Customer.Id
INNER JOIN Item ON Item.Id = Order.Product_Id
GROUP BY
Customer.Name) AS Data
A subquery to get the count of currencies and then ask for them in the main query to show the total or the text "Multiple-currencies".
Sorry if there is any mistake or mistype but I don't have a database server to test it
Hope this helps.
IMO I would start by standardizing variable names. Why call ID in customer table USER_ID in order table? Just a pet peeve. Anyway, you should learn how to build queries.
start with joining the customer table to the order table on then join the result to the item table. The first join is on CUSTOMER_ID and the second join is on PRODUCT_ID. Once you have that working use SUM and GROUP BY
Ok, I managed to solve the problem this way:-
SELECT innerQuery.Name AS Name, (CASE WHEN innerQuery.Currencies=1 THEN (innerQuery.Amount || innerQuery.Currency) ELSE 'Mutliple-Currencies' END) AS Amount, FROM
(SELECT Customer.Name, SUM(Item.Amount), COUNT(DISTINCT Item.Currency) AS Currencies, Item.Currency AS Currency FROM Customer
INNER JOIN Order ON Order.User_Id = Customer.Id
INNER JOIN Item ON Item.Id = Order.Product_Id
GROUP BY Customer.Name)innerQuery

Select columns with and without group by

Having Table1
id | productname | store | price
-----------------------------------
1 | name a | store 1 | 4
2 | name a | store 2 | 3
3 | name b | store 3 | 6
4 | name a | store 3 | 4
5 | name b | store 1 | 7
6 | name a | store 4 | 5
7 | name c | store 3 | 2
8 | name b | store 6 | 5
9 | name c | store 2 | 1
I need to get all columns but only the rows with the
lowest price.
Result needed:
id | productname | store | price
-----------------------------------
2 | name a | store 2 | 3
8 | name b | store 6 | 5
9 | name c | store 2 | 1
My best try is:
SELECT ProductName, MIN(Price) AS minPrice
FROM Table1
GROUP BY ProductName
But then I need the ID and STORE for each row.
Try this
select p.* from Table1 as p inner join
(SELECT ProductName, MIN(Price) AS minPrice FROM Table1 GROUP BY ProductName) t
on p.productname = t.ProductName and p.price = t.minPrice
Select ID,ProductName,minPrice
from
(
SELECT ProductName, MIN(Price) AS minPrice
FROM Table1
GROUP BY ProductName
) t
join Table1 t1 on t.ProductName = t1.ProductName
You didn't mention your SQL dialect, but most DBMSes support Standard SQL's "Windowed Aggregate Functions":
select *
from
( select t.*,
RANK() OVER (PARTITION BY ProductName ORDER BY Price) as rnk
from table1 as t
) as dt
where rnk = 1
If multiple stores got the same lowest price all of them will be returned. If you want only a single shop you have to switch to ROW_NUMBER instead of RANK or add column(s) to the ORDER BY.
I think this query should do:
select min(t.id) id
, t.productname
, t.price
from table1 t
join
( select min(price) min_price
, productname
from table1
group
by productname
) v
on v.productname = t.productname
and v.price = t.min_price
group
by t.productname
, t.price
It determines the lowest price per product and fetches every line in the base table (t). This avoids duplicates by grouping on the productname and selecting the lowest id.
This should work for you:
SELECT * FROM `Table1` AS `t1`
WHERE (
SELECT count(*) FROM `Table1` AS `t2` WHERE `t1`.`productName` = `t2`.`productName` AND `t2`.`price` < `t1`.`price`) < 1
Check SqlFiddle
But if you have same products with same minimum price in two stores, you will get both of them in result output

Need to fetch Total Rows Count and Rows count based on Group by in single Query

I have table REQUEST
REQUESTID` | ProductID
----------------------------
1 | 1
2 | 1
3 | 4
4 | 4
5 | 4 `
Now i need output as within single query
ProductID | Count | Total
----------------------------------------------------
1 | 2 | 5
4 | 3 | 5
Basically, i need to calculate Product Percentage amongs total request how many percentage people prefer particular product. And i need this to be done in single query
Code What I have tried ::
Alter Proc SP_Get_Product_History_Count
AS
Declare #Tot bigint
Select #Tot = COUNT(RequestID) from Request
Select
Pro.ProductName,
COUNT(Req.RequestID) /#Tot as Count
From
Request As Req
inner join
Product As Pro
on
Req.ProductID = Pro.ProductID
Group by Pro.ProductName
;WITH x AS
(
SELECT ProductID, total = COUNT(*) OVER()
FROM dbo.REQUEST
)
SELECT
ProductID,
COUNT(*),
MAX(total),
1.0*COUNT(*)/MAX(total)
FROM x
GROUP BY ProductID;
SQLFiddle demo
select productid,
count(requestid) as [Count],
(select count(requestid) from [Request]) as Total
from [Request]
group by productid

Sql: Calc average times a customers ordered a product in a period

How would you calc how many times a product is sold in average in a week or month, year.
I'm not interested in the Amount, but how many times a customer has bought a given product.
OrderLine
OrderNo | ProductNo | Amount |
----------------------------------------
1 | 1 | 10 |
1 | 4 | 2 |
2 | 1 | 2 |
3 | 1 | 4 |
Order
OrderNo | OrderDate
----------------------------------------
1 | 2012-02-21
2 | 2012-02-22
3 | 2012-02-25
This is the output I'm looking for
ProductNo | Average Orders a Week | Average Orders a month |
------------------------------------------------------------
1 | 3 | 12 |
2 | 5 | 20 |
You would have to first pre-query it grouped and counted per averaging method you wanted. To distinguish between year 1 and 2, I would add year() of the transaction into the grouping qualifier for distinctness. Such as Sales in Jan 2010 vs Sales in 2011 vs 2012... similarly, week 1 of 2010, week 1 of 2011 and 2012 instead of counting as all 3 years as a single week.
The following could be done if you are using MySQL
select
PreCount.ProductNo,
PreCount.TotalCount / PreCount.CountOfYrWeeks as AvgPerWeek,
PreCount.TotalCount / PreCount.CountOfYrMonths as AvgPerMonth,
PreCount.TotalCount / PreCount.CountOfYears as AvgPerYear
from
( select
OL.ProductNo,
count(*) TotalCount,
count( distinct YEARWEEK( O.OrderDate ) ) as CountOfYrWeeks,
count( distinct Date_Format( O.OrderDate, "%Y%M" )) as CountOfYrMonths,
count( distinct Year( O.OrderDate )) as CountOfYears
from
OrderLine OL
JOIN Order O
on OL.OrderNo = O.OrderNo
group by
OL.ProductNo ) PreCount
This is a copy of DRapp's answer, but coded for SQL Server (it's too big for a comment!)
SELECT PreCount.ProductNo,
PreCount.TotalCount / PreCount.CountOfYrWeeks AS AvgPerWeek,
PreCount.TotalCount / PreCount.CountOfYrMonths AS AvgPerMonth,
PreCount.TotalCount / PreCount.CountOfYears AS AvgPerYear
FROM (SELECT OL.ProductNo,
Count(*) TotalCount,
Count(DISTINCT Datepart(wk, O.OrderDate)) AS CountOfYrWeeks,
Count(DISTINCT Datepart(mm, O.OrderDate)) AS CountOfYrMonths,
Count(DISTINCT Year(O.OrderDate)) AS CountOfYears
FROM OrderLine OL JOIN [Order] O
ON OL.OrderNo = O.OrderNo
GROUP BY OL.ProductNo) PreCount