two counts in group by - sql

I want to find for each store the number of products for that store/number of customers of that store
Customer
Id | dealerId
1 | 10
2 | 11
3 | 10
Product
Id | CustomerId
100 | 1
200 | 3
300 | 2
store
Id
10
11
The result for the above example would be:
StoreId | value
10 | 2/2
11 | 1/1

SELECT
c.dealerID as Store,
COUNT(DISTINCT c.ID) as NumOfCustomers,
COUNT(p.ID) as NumOfProducts
FROM
Customers C
INNER JOIN
Products p on p.CustomerId = c.Id
GROUP BY
c.dealerID

select S.Id AS StoreId, COUNT(p.Id) AS ProductId, COUNT(CAST.Id) AS CustomerId
from Store S
left join Customer C on C.dealerId = Scheduler.StoreId
left join Product P on P.CustomerId = CAST.Id
group by S.Id

Related

How can I get the row with a min value from a group and the associated values from another tables?

So I have three tables (Products, Stock and ProductsBatches ) and I'm trying to get the batch that expires first (from ProductsBatches) for each product and some additional info from Products (like product name) and Stock (like total quantity)
I managed to get just the earliest date for each product but when I add some other columns and joins it messes it up and it gets me all sorts of dates.
PRODUCTS TABLE:
| PRODUCT ID (PK)| NAME | PRICE |
| 1 | CHEESE | 12.0 |
STOCK TABLE :
| PRODUCT ID (PFK)| TOTAL QUANTITY |
| 1 | 100 |
PRODUCTS BATCHES TABLE:
| PRODUCT ID (PFK) | BATCH (PFK) | BATCH QUANTITY | BATCH EXPIRY |
| 1 | XYZ | 50 | 2019-01-01 |
| 1 | XZZ | 50 | 2020-01-01 |
So far I've got:
| PRODUCT ID | BATCH EXPIRY |
| | |
| 1 | 2019-01-01 |
With SELECT productID,min(batchExpiry) from PRODUCTSBATCHES group by (productID)
What I need:
|PRODUCT ID| NAME |TOTAL QUANTITY|PRICE|BATCH|BATCH QUANTITY|BATCH EXPIRY|
| 1 |CHEESE| 100 |12.0 | XYZ | 50 | 2019-01-01 |
I tried this but is also not good :
SELECT DISTINCT ON (b.productID) b.productID, p.name, s.totalquantity,
p.price, b.batch, b.batchquantity, b.batchExpiry
FROM productsbatches as b
INNER JOIN (
SELECT productID,min(batchExpiry) from PRODUCTSBATCHES group by (productID)
) as exmin b.productID = exmin.productID
INNER JOIN stock as s ON b.productID = s.productID
INNER JOIN products as p ON s.productID = p.productID
Thanks.
You are close, but you forgot to join the batchExpiry column with the min batchExpiry derived by the subquery:
SELECT
p.productID, p.name, s.totalquantity,
p.price, b.batch, b.batchquantity, b.batchExpiry
FROM products as p
INNER JOIN stock as s ON p.productID = s.productID
INNER JOIN productsbatches as b ON p.productID = b.productID
INNER JOIN (
SELECT productID, min(batchExpiry) as batchExpiry
FROM PRODUCTSBATCHES
GROUP BY productID
) as exmin ON b.productID = exmin.productID AND b.batchExpiry = exmin.batchExpiry
DISTINCT ON is the right way to go. You just have to use it correctly:
SELECT DISTINCT ON (p.productID) p.productID, p.name, s.totalquantity,
p.price, b.batch, pb.batchquantity, pb.batchExpiry
FROM products p INNER JOIN
stock s
ON p.productID = s.productID INNER JOIN
productsbatches pb
ON pb.productID = p.productID
ORDER BY p.productID, pb.batchExpiry;
Basically, all you need is the ORDER BY. The subquery is not necessary.

How can I select customers who have certain number of orders and other conditions?

I am studying SQL. There is one particular query which I find difficult to implement (using PostgreSQL database).
Could someone help me with that?
Query : "Who are the customers who have at least one order which includes products from at least 3 product categories?".
The result should include 2 columns: customerID and Quantity of orders.
The tables are the following:
Product ProductID (id of a product)|ProductCategoryID (product categories) both integers.
SalesOrderDetail :SalesOrderID (id order)|SalesOrderDetailID|OrderQty|ProductID, all of them integers .
SalesOrderHeader: SalesOrderID|CustomerID, both integers.
The only thing I got so far is the first part of the statement:
select salesorderheader.customerID, salesorderdetail.orderqty
from salesorderheader, salesorderdetail;
Sample data:
productid | productcategoryid
-----------+-------------------
1 | 2
2 | 2
3 | 3
4 |
salesorderid | salesorderdetailid | orderqty | productid
--------------+--------------------+----------+-----------
43659 | 1 | 1 | 776
43659 | 2 | 3 | 777
43659 | 3 | 1 | 778
salesorderid | customerid
--------------+------------
43659 | 29825
43660 | 29672
43661 | 29734
I see two approaches here:
1) Generate a subquery which contains the count a salesOrderID and the count of Distinct ProductCategories used for that salesorder.
SELECT customerID, count(Distinct OH.SalesOrderID) SalesOrdersWithMoreThan3Categories
FROM SalesOrderHeader OH
INNER JOIN (SELECT OD.SalesOrderID, Count(Distinct P.ProductCategoryID) Cnt
FROM SalesOrderDetail OD
INNER JOIN Product P
on P.ProudctID = OD.ProductID
GROUP BY OD.SalesOrderID) B
on OH.SalesOrderID = B.SaleOrderID
and B.cnt >=3
GROUP BY OH.customerID
2) Use Exists to identify orderDetails a count of distinct productCategories >=3
SELECT OH.customerID, count(Distinct OH.SalesOrderID) SalesOrdersWithMoreThan3Categories
FROM SalesOrderHeader OH
WHERE exists (SELECT 1
FROM SalesOrderDetail OD
INNER JOIN Product P
on P.ProudctID = OD.ProductID
WHERE OH.SalesOrderID = OD.SalesOrderID
GROUP BY OD.SalesOrderID
HAVING Count(Distinct P.ProductCategoryID) >=3)
GROUP BY OH.customerID

How to get sum / Qty of same records in a table

I have two tables Company and CompanyLike
CompanyLike
Id
RatingStar
Count
Companyid
Company and CompanyLike have 1 to Many relation . One company can have many likes
Company Like Table Will Look Like this
+----------------------------+
| ID RatingStar Companyid |
+----------------------------+
| 1 5 5636 |
| 2 5 5636 |
| 3 1 101 |
| 4 2 959 |
+----------------------------+
Now i want to get count of rating star that how many times RatingStar is 5 , and how many time its 2 against every Companyid
My current working is
SELECT RatingStar AS RatingStar,com.id AS Companyid , c.Id AS Id ,
com.Name AS CompanyName, Count(RatingStar) AS ratingCount
FROM CompanyReviewLike AS c
INNER JOIN Company AS com ON com.id = c.Companyid
GROUP BY RatingStar ,com.Name , com.id ,c.Id
I think you just want to remove c.id from your query:
select RatingStar as RatingStar,com.id as Companyid ,com.Name as
CompanyName ,
Count(RatingStar) as Count,MIN(c.ID) as Id
from CompanyReviewLike as c
inner join Company as com on com.id = c.Companyid
Group By
RatingStar ,com.Name , com.id
Since every row has a different c.id, if you include it in the grouping then every group only contains one row. If you wanted to include one id value in the result, then pick a suitable aggregate (MIN or MAX spring to mind) to select one from the group.
Fiddle. Result:
RATINGSTAR COMPANYID COMPANYNAME COUNT
1 101 CompanyB 1
2 959 CompanyC 1
5 5636 CompanyA 2
Please check this if you are only concern for rating-star 5 and 2 counts
SELECT COM.Name AS CompanyName, C.RatingStar AS RatingStar, COUNT(*) AS RatingCount
FROM CompanyLike AS C
INNER JOIN Company AS COM ON COM.id = C.Companyid
GROUP BY C.RatingStar ,COM.Name
HAVING C.RatingStar IN (5,2)
Check this latest one
SELECT C.id,COM.Name AS CompanyName, C.RatingStar AS RatingStar, COM.RatingCount AS RatingCount
FROM CompanyLike C
INNER JOIN ( SELECT COM.Name AS CompanyName,C.CompanyId, C.RatingStar AS RatingStar, COUNT(*) AS RatingCount
FROM CompanyLike AS C
INNER JOIN Company AS COM ON COM.id = C.Companyid
GROUP BY C.RatingStar ,COM.Name ,C.CompanyId
HAVING C.RatingStar IN (5,2)) COM
ON C.RatingStar = Com.RatingStar AND C.CompanyId = COM.id
You need to pivot data. Le'ts supose your tables are:
create table compa ( id int, name varchar(10) );
create table likes (idComp int, rating int);
create table rates ( id int);
Then your query is:
select *
from (
select r.id as rate,
c.id as comp,
l.idComp as n
from compa c
cross join rates r
left join likes l
on l.rating = r.id
and c.id = l.idComp
) as src
PIVOT
(
count( n )
FOR rate IN ( [1], [2], [3], [4], [5] )
) as pvt ;
And results:
| COMP | 1 | 2 | 3 | 4 | 5 |
----------------------------
| 1 | 3 | 1 | 0 | 0 | 1 |
| 2 | 0 | 3 | 0 | 0 | 0 |
.
Explanation
Select inner query join results between tables to get rates raw data. Pivot clause counts data and move row data to columns to get only a column by company.
Notice rates cross join to get all rates from a company, also left join to get no rates companies.
Enjoy.

SELECT all orders with more than one item and check all items status

I have 2 tables:
Orders:
Id | Status
-----+--------
1 | OK
2 | WAITING
3 | WAITING
4 | OK
5 | OK
6 | OK
And Order_details:
Id | Order_Id | Status
-----+------------+--------
1 | 1 | S1
2 | 1 | S1
3 | 2 | S1
4 | 4 | S2
5 | 4 | S2
6 | 4 | S3
7 | 5 | S1
8 | 5 | S1
What I need to do is to select all orders having more than one Order_detail and Order status must be 'OK' and every order_detail must have status in (S1,S2)
I've done something like this:
SELECT O.Id FROM Orders O
JOIN Order_details OD
ON O.Id=OD.Order_Id
WHERE O.Status='OK' AND OD.Status IN ('S1','S2')
GROUP BY O.Id
HAVING count(DISTINCT OD.Id)>1
But this returns all Orders that have more than one Order_details meeting criteria.
I'm sure that this part is fine:
SELECT O.Id FROM Orders O
JOIN Order_details OD
ON O.Id=OD.Order_Id
WHERE O.Status='OK'
GROUP BY O.Id
HAVING count(DISTINCT OD.Id)>1
But I must check if every Order_detail for above orders has Status IN (S1, S2).
So if Order will have 2 details and one of them has status=S1 and second S3 this order should be skipped.
SELECT O.Id
FROM Orders O
JOIN Order_details OD ON O.Id=OD.Order_Id
WHERE O.Status='OK'
GROUP BY O.Id
HAVING count(DISTINCT OD.Id)>1
AND SUM(CASE WHEN OD.Status NOT IN ('S1','S2')
THEN 1
ELSE 0
END) = 0
select O.id
from orders O inner join order_details OD
on O.Id=OD.Order_Id and O.Status='OK' AND OD.Status IN ('S1','S2')
GROUP BY O.Id
HAVING count(DISTINCT OD.Id)>1

How to query count(customer), count(order) pair

Assuming we have tables
Order(customerId,orderDetails...)
Customer(Id, customerDetails...)
What is the easiest way to query Count(customer)xCount(order) pair?
example
Customer:
ID | Name
---------
1 | Bob
2 | Ann
Order
CustomerId | Address
--------------------
1 | Block1
1 | Block2
1 | Block1
2 | Home Address
Want to get
CustomerCount | OrderCount
--------------------------
1 | 3
1 | 1
How about
SELECT DISTINCT COUNT(DISTINCT c.ID) AS CustomerCount
, COUNT(*) AS OrderCount
FROM Customer AS c
INNER JOIN [Order] AS o ON o.customerID = c.ID
GROUP BY
c.ID
SQL Fiddle
Edit
Coming to think of it, the previous statement can be reduced to this
SELECT DISTINCT 1 AS CustomerCount
, COUNT(*) AS OrderCount
FROM Customer AS c
INNER JOIN [Order] AS o ON o.customerID = c.ID
GROUP BY
c.ID
but that gives me a nagging feeling that my initial statement is wrong to begin with
select order.id, count(.CustomerId )
from Order order left join Customer customer on order.id = customer.CustomerId
group by order.id