Using COUNT with MAX in SQL - sql

I am trying to find which customer has the most transactions. Transaction table has an foreign key that identifies each transaction with a customer. What I currently is the following code:
WITH Customers as (
SELECT
[CustName] as 'Customer',
[TRANSACTION].[CustID] as 'Total # of Transactions'
FROM [dbo].[CUSTOMER]
INNER JOIN [dbo].[TRANSACTION]
ON [CUSTOMER].[CustID] = [TRANSACTION].[CustID]
)
SELECT *
FROM Customers
WHERE 'Total # of Transactions' = (SELECT MAX('Total # of Transactions') FROM Customers);
Two things are wrong:
1) The latter part of the code doesn't accept 'Total # of Transactions'. If I were to rename it to a single word, I could treat it kind of like a variable.
2) My last SELECT statement gives me a result of the customer and all their transactions, but doesn't give me a COUNT of those transactions. I'm not sure how to use COUNT in conjunction with MAX.

First select customers and transaction count.
Then select the largest one.
Them limit your select to that item.
Work you way from the inside out.
SELECT *
FROM Customers
WHERE CustID =
(
SELECT TOP 1 CustID
FROM (SELECT CustID, COUNT(*) AS TCOUNT
FROM TRANSACTIONS
GROUP BY CustID) T
ORDER BY T.TCOUNT DESC
) TT

This should get you everything you need. To get the top customer just add Top 1 to the select
WITH Customers as (
SELECT
[CustName] as Name
FROM [dbo].[CUSTOMER]
INNER JOIN [dbo].[TRANSACTION]
ON [CUSTOMER].[CustID] = [TRANSACTION].[CustID]
)
-- to get count of transactions
Select Count(*) as count, Name
FROM Customers
Group by Name
Order By Count(*) desc

Your inner table just returns CustID as a total number of transactions? You need to start by finding the total count for each customer. Also for a column you can use [Name], when you use apostrophes it thinks you are comparing a string. If you want to return all customers with the highest count, you could use this:
WITH TransactionCounts as (
SELECT
CustID,
COUNT(*) AS TransactionCount
FROM [dbo].[TRANSACTION]
GROUP BY CustID
)
SELECT TOP 1 CUSTOMER.*, TransactionCount
FROM TransactionCounts
INNER JOIN CUSTOMER ON CUSTOMER.CustID = TransactionCounts.CustId
ORDER BY TransactionCount DESC
-- alternate to select all if multiple customers are tied for highest count
--WHERE TransactionCount = (SELECT MAX(TransactionCount) FROM TransactionCounts)

Related

SQL Bigquery - Create a data set from 3 tables with master records and no duplicates with ARRAY_AGG(Struct)

I am trying to query 3 tables to create one data set with master item data, transaction data and then serial numbers/locations.
My left table is a unique key for each item mentioned once, then the transaction data and serial data has the key repeating many times and I would like them to be a collection under the item for use in Looker Studio.
I have this working for Item to Transaction and Item to Serial but when I put all together I get loops and repeats of Data.
Here is an example of the tables
enter image description here
Here is the desired output
enter image description here
ItemCode
Manufacturer
Model
Qty on Order
Transaction
Date
Qty
Location
WH-Bin
Serial
Qty
Here is the code I have working to join two of the tables...
with nested as (
SELECT
i.ItemCode,i.Manuf,i.ItemName,i.QtyonOrder,i.AvgPrice,
array_agg(struct (t.Date, t.Quanitty)) as Transactions,
FROM
ItemMasterData i
Left join
Transactions t on i.ItemCode = t.ItemCode
group by i.ItemCode,i.Manuf,i.ItemName,i.QtyonOrder,i.AvgPrice,
)
Select * from nested,
When I tied adding another
array_agg(struct (l.WHBin, l.Serial, l.Qty)) as Transactions,
Left Join Transactions l on i.ItemCode = l.ItemCode
I ended up having loops filling the blanks of inventory and transaction data.
I guess you want to use the sub select to generate arrays inside a table.
With ItemMasterData as (Select *, rand()*1000 as info_fields from unnest(["IT0001","IT0002"]) ItemCode ),
Transactions as (Select ItemCode, Date from ItemMasterData,unnest(generate_date_array("2022-01-01","2022-01-03")) as Date),
Location as (Select ItemCode, rand()*100 as loc from unnest(["IT0001","IT0002","IT---"]) ItemCode,unnest(generate_array(1,5)) as Date)
select ItemCode , info_fields,
((Select array_agg(struct(date,"..." as other_fields)) from Transactions t where i.ItemCode=t.ItemCode group by ItemCode )) as Transactions,
((Select array_agg(struct(loc,"---" as another_fields)) from Location L where i.ItemCode=L.ItemCode group by ItemCode )) as Locations,
from
ItemMasterData i
Another way is to generate a row number and combine the tables by this as well.
With ItemMasterData as (Select *, rand()*1000 as info_fields from unnest(["IT0001","IT0002"]) ItemCode ),
Transactions as (Select ItemCode, Date from ItemMasterData,unnest(generate_date_array("2022-01-01","2022-01-03")) as Date),
Location as (Select ItemCode, rand()*100 as loc from unnest(["IT0001","IT0002","IT---"]) ItemCode,unnest(generate_array(1,5)) as Date)
select * #except(dummy_row),
from
ItemMasterData
Left join
(Select *, row_number() over (partition by ItemCode) as dummy_row from Transactions)
using(ItemCode)
full join
(Select *,row_number() over (partition by ItemCode) as dummy_row from Location)
using(ItemCode,dummy_row)
group by 1
order by 1,2

Count on Table 1 based on Count with Clause on Table 2, sql

Table 1
Table 2
I need to find the Count of total number of unique stores that have "Achieved Date" not Null that achieved all of the "Achievement Ids" "enabled" on Table 2.
So far I can find the count of stores that achieved a hard coded number, but I'm not breaking through the part where I use the Count of Enabled Ids on table 2 to define what the number is.
SELECT
COUNT(*) AS count
FROM
(SELECT
StoreNumber, COUNT(*) as Achievements
FROM
StoreAchievementProgress
WHERE
AchievedDate IS NOT NULL
GROUP BY
StoreNumber) count
maybe this query
SELECT S.StoreNumber
FROM StoreAchievementProgress S
RIGHT JOIN (SELECT Id FROM Table2 WHERE Enabled=1 )T
ON T.Id=S.AchievementId
AND AchievedDate IS NOT NULL
GROUP BY S.StoreNumber
HAVING COUNT(1) = (SELECT COUNT(Id) FROM Table2 WHERE Enabled=1 )
Joining the stores with a count of their enabled achievements to how many they can get
SELECT COUNT(*) AS StoresFullAchievements
FROM
(
SELECT p.StoreNumber, COUNT(*) AS TotalEnabledAchievements
FROM StoreAchievementProgress p
JOIN Achievements a ON a.id = p.AchievementId
WHERE p.AchievedDate IS NOT NULL
AND a.Enabled = 1
GROUP BY p.StoreNumber
) AS s
JOIN
(
SELECT COUNT(*) AS TotalEnabled
FROM Achievements
WHERE Enabled = 1
) a
ON a.TotalEnabled = s.TotalEnabledAchievements

MS-Access: HAVING clause not returning any records

I have a Select query to extract Customer Names and Purchase Dates from a table. My goal is to select only those names and dates for customers who have ordered on more than one distinct date. My code is as follows:
SELECT Customer, PurchDate
FROM (SELECT DISTINCT PurchDate, Customer
FROM (SELECT CDate(FORMAT(DateAdd("h",-7,Mid([purchase-date],1,10)+""+Mid([purchase-date],12,8)), "Short Date")) AS PurchDate,
[buyer-name] AS Customer
FROM RawImport
WHERE sku ALIKE "%RE%"))
GROUP BY Customer, PurchDate
HAVING COUNT(PurchDate)>1
ORDER BY PurchDate
This returns no results, even though there are many customers with more than one Purchase Date. The inner two Selects work perfectly and return a set of distinct dates for each customer, so I believe there is some problem in my GROUP/HAVING/ORDER clauses.
Thanks in advance for any help!
You are doing in the inner select
SELECT DISTINCT PurchDate, Customer
and in the outter select
GROUP BY Customer, PurchDate
That mean all are
having count(*) = 1
I cant give you the exact sintaxis in access but you need something like this
I will use YourTable as a replacement of your inner derivated table to make it easy to read
SELECT DISTINCT Customer, PurchDate
FROM YourTable
WHERE Customer IN (
SELECT Customer
FROM (SELECT DISTINCT Customer, PurchDate
FROM YourTable)
GROUP BY Customer
HAVING COUNT(*) > 1
)
inner select will give you which customer order on more than one day.
outside select will bring you those customer on all those days.
.
Maybe you can try something simple to get the list of customer who brought in more than one day like this
SELECT [buyer-name]
FROM RawImport
WHERE sku ALIKE "%RE%"
GROUP BY [buyer-name]
HAVING Format(MAX(purchase-date,"DD/MM/YYYY")) <>
Format(MIN(purchase-date,"DD/MM/YYYY"))

SQL server sum and count

I've got two tables:
orders:
orderId
orderTotal
state
orderStatesHistory
id
orderId
stateId
I need to display get to display results like this.
Right now my code is this, I'm using SQL Server
WITH totalOrders AS
(
SELECT orders.state, COUNT(1) AS counter, SUM(orders.orderTotal) AS total
FROM orders
CROSS APPLY
(
SELECT orderId, currentDateTime FROM orderStatesHistory
WHERE (orderStatesHistory.stateId = 1 OR orderStatesHistory.stateId = 3)
AND NULLIF(orderStatesHistory.subStateId, 0) IS NULL
AND orderStatesHistory.currentDateTime BETWEEN '2015-07-28 00:00:00' AND '2015-08-04 00:00:00'
AND orders.id = orderStatesHistory.orderId
) AS statesHistory
WHERE orders.state IN (1,2,3,4,5)
AND orders.documentType = 1
GROUP BY orders.state
)
SELECT 9999 AS state, SUM(counter) AS counter, SUM(total) AS total
FROM totalOrders
UNION
SELECT state, counter, total
FROM totalOrders
Problem is somehow, registries in orderStatesHistory might be duplicated, and I only want to use each orderId once in "count()" and "sum()"
I've been struggling pretty hard, not sure if i'll be able to do it all with SQL, maybe some genius helps me out, if not I'll do it throught the software.
NOTE: When I do count() and sum(), I want to only use one time each orderId, if they are duplicated i don't want to count them.
Any help is apreciated, even someone saying it is imposible.
PD: I'm willing to use JOINS if necesary, no need to use SQL server specific language.
UPDATE 1:
Data in orderStatesHistory
Data in orders
You should be able to accomplish this using a JOIN and DISTINCT instead of a CROSS APPLY. If you could post some sample data, it would help in verifying.
WITH totalOrders AS
(
SELECT orders.state, COUNT(1) AS counter, SUM(orders.orderTotal) AS total
FROM orders
INNER JOIN
(
SELECT DISTINCT orderId FROM orderStatesHistory
WHERE (orderStatesHistory.stateId = 1 OR orderStatesHistory.stateId = 3)
AND NULLIF(orderStatesHistory.subStateId, 0) IS NULL
AND orderStatesHistory.currentDateTime BETWEEN '2015-07-28 00:00:00' AND '2015-08-04 00:00:00'
) AS statesHistory
ON orders.id = statesHistory.orderId
WHERE orders.state IN (1,2,3,4,5)
AND orders.documentType = 1
GROUP BY orders.state
)
SELECT 9999 AS state, SUM(counter) AS counter, SUM(total) AS total
FROM totalOrders
UNION
SELECT state, counter, total
FROM totalOrders

How do I combine SELECT statements to allow me to calculate percentages, successes and failures in SQL Server?

Imagine a table :
CUST_PROMO (customer_id,PROMOTION) which is used as a mapping between every promotion that customer have received.
select promotion, count(customer_id) as promo_size
from CUST_PROMO
group by promotion
This gets us the total number of customers in each promotion.
Now, we've got CUSTOMER (customer_id, PROMO_RESPONDED,PROMO_PURCHASED), which lists the customer and which promotion got the customer to respond, and which got them to purchase.
select PROMO_RESPONDED, count(customer_id) as promo_responded
from CUSTOMER
group by PROMO_RESPONDED
select PROMO_PURCHASED,count(customer_id) as promo_responded
from CUSTOMER
group by PROMO_PURCHASED
This is all very self-explanatory; now I've got the number of people for whom each promo was successful.
But; what I'd like to end up with is [in CSV form]
PROMOTION,PROMO_SIZE,PROMO_RESPONDED,PROMO_PURCHASED,PROMO_RESPSUCCESSRATE,blah
1,100,12,5,12%,...
2,200,23,14,11.5%,...
I have no idea how to do this. I can UNION the three queries above; but that doesn't actually result in what I want. I thought about creating an in-memory table, inserting in each promo value and then doing an update statement with a join against it to set the values each -- but that's pretty messy; and requires a new UPDATE statement for each table/select statement. I could also make a temp table per result set and then join them together; but really; who wants to do that?
I can't think of any way of joining this data that makes any sense; since I'm dealing with aggregates.
So, at best, I need a function that, like UNION, will combine result sets, but will actually combine like columns on a key and ADD those columns rather than union which adds rows. The description makes it sound like a JOIN; but I can't see that working.
Thanks for the help!
SELECT
cp.promotion,
PROMO_SIZE = COUNT(*),
PROMO_RESPONDED = COUNT(c1.customer_id),
PROMO_PURCHASED = COUNT(c2.customer_id),
PROMO_RESPSUCCESSRATE = COUNT(c1.customer_id) * 100.0 / COUNT(*)
FROM CUST_PROMO cp
LEFT JOIN CUSTOMER c1
ON cp.customer_id = c1.customer_id AND cp.promotion = c1.PROMO_RESPONDED
LEFT JOIN CUSTOMER c2
ON cp.customer_id = c2.customer_id AND cp.promotion = c2.PROMO_PURCHASED
GROUP BY cp.promotion
WITH tmp AS
(
SELECT PROMOTION, 0 as promo_responded, 0 as promo_purchased, COUNT(customer_id) as total
FROM CUST_PROMO
GROUP BY PROMOTION
SELECT PROMOTION, COUNT(customer_id) as promo_responded, 0 as promo_purchased, 0 as total
FROM CUSTOMER
GROUP BY PROMO_RESPONDED
UNION
SELECT PROMOTION, COUNT(customer_id) as promo_purchased, 0 as promo_responded, 0 as total
FROM CUSTOMER
GROUP BY PROMO_PURCHASED
)
SELECT PROMOTION, SUM(promo_responded) as TotalResponded, SUM(promo_purchased) as TotalPurchased, SUM(Total) as TotalSize,
SUM(promo_responded)/SUM(Total) as ResponseRate, SUM(promo_purchased)/SUM(Total) as PurchaseRate
FROM tmp
Does this work? I'm not sure about division and multiplication operators, but I belive my logic is good.The key is using corelated select substatements in the select statement.
SELECT c.promotion,
COUNT(c.customer_id) as promo_size,
(SELECT COUNT(customer_id)
FROM CUSTOMER
WHERE PROMO_RESPONDED = c.promotion) PROMO_RESPONDED,
(SELECT COUNT(customer_id)
FROM CUSTOMER
WHERE PROMO_PURCHASED = c.promotion) PROMO_PURCHASED,
(SELECT COUNT(customer_id) *100/count(c.customer_id)
FROM CUSTOMER
WHERE PROMO_RESPONDED = c.promotion)
FROM CUST_PROMO c
GROUP BY c.promotion
A cleaner solution using decode. Still not sure the math is working
select PROMOTION, count(CUSTOMER_ID) as promo_size,
SUM(DECODE(PROMO_RESPONDED, PROMOTION, 1, 0)) PROMO_RESPONDED,
SUM(DECODE(PROMO_PURCHASED, PROMOTION, 1, 0)) PROMO PURCHASED,
SUM(DECODE(PROMO_RESPONDED, PROMOTION, 1, 0))*100/count(CUSTOMER_ID) PROMO_RESPONDED
from CUST_PROMO join CUSTOMER using CUSTOMER_ID
group by PROMOTION
Yes, I think JOINing the three aggregate queries is the way to go. The LEFT JOINs are there just in case some promotion get no response or no purchases.
I also changed the COUNT(customer_id) to COUNT(*). The result is the same, unless customer_id field can have NULL values in the two tables which most probably is not the case. If however, a customer may appear in two rows of a table with same promotion code, then you should change those into COUNT(DISTINCT customer_id) :
SELECT prom.promotion
, prom.promo_size
, responded.promo_responded
, purchased.promo_purchased
, responded.promo_responded / prom.promo_size
AS promo_response_success_rate
FROM
( SELECT promotion
, COUNT(*) AS promo_size
FROM CUST_PROMO
GROUP BY promotion
) AS prom
LEFT JOIN
( SELECT PROMO_RESPONDED AS promotion
, COUNT(*) AS promo_responded
FROM CUSTOMER
GROUP BY PROMO_RESPONDED
) AS responded
ON responded.promotion = prom.promotion
LEFT JOIN
( SELECT PROMO_PURCHASED AS promotion
, COUNT(*) AS promo_purchased
FROM CUSTOMER
GROUP BY PROMO_PURCHASED
) AS purchased
ON purchased.promotion = prom.promotion