SQL 2 Rows into 1 - sql

Below is a SQL script to see how many customers purchased both products, as well as how many customers purchased either of the two products.
I would like to be able to return a result set with one column for the first product, one column for the second, one column for Count with Both, and one column for Count with Either.
Instead it returns a column for each of the counts and a single NULL column for the ProductID.
SELECT
COUNT(DISTINCT b.CustomerID) AS "Count with Both",
COUNT(DISTINCT c.CustomerID) AS "Count with Either",
b.ProductID
FROM
LocationCode z
LEFT JOIN
(SELECT DISTINCT ProductID, CustomerID
FROM LocationCode a
WHERE a.ProductID IN ('MP040') AND a.ProductID IN ('OG010')) b ON z.CustomerID = b.CustomerID
LEFT JOIN
(SELECT DISTINCT ProductID, CustomerID
FROM LocationCode b
WHERE b.ProductID IN ('MP040', 'OG010')) c ON z.CustomerID = c.CustomerID
GROUP BY
b.ProductID

If I understand correctly, you can use a self-join and aggregation. The following does what you want for all pairs of products:
with cp as (
select customerid, productid,
count(*) over (partition by customerid) as num_customers
from locationcode
group by customerid, productid
)
select cp1.productid, cp2.productid, cp1.cnt as cnt1, cp2.cnt as cnt2,
count(*) as both
from cp cp1 join
cp cp2
on cp1.customerid = cp2.customerid and
cp1.productid < cp2.productid
group by cp1.productid, cp2.productid, cp1.cnt, cp2.cnt ;
You can of course add where filters if you only want results for certain products.

Related

Sql query to display records that appear more than once in a table

I have two tables, Customer with columns CustomerID, FirstName, Address and Purchases with columns PurchaseID, Qty, CustomersID.
I want to create a query that will display FirstName(s) that have bought more than two products, product quantity is represented by Qty.
I can't seem to figure this out - I've just started with T-SQL
You could sum the purchases and use a having clause to filter those you're interested in. You can then use the in operator to query only the customer names that fit these IDs:
SELECT FirstName
FROM Customer
WHERE CustomerID IN (SELECT CustomerID
FROM Purchases
GROUP BY CustomerID
HAVING SUM(Qty) > 2)
Please try this, it should work for you, according to your question.
Select MIN(C.FirstName) FirstName from Customer C INNER JOIN Purchases P ON C.CustomerID=P.CustomersID Group by P.CustomersID Having SUM(P.Qty) >2
Please try this:
select c.FirstName,p.Qty
from Customer as c
join Purchase as p
on c.CustomerID = p.CustomerID
where CustomerID in (select CustomerID from Purchases group by CustomerID having count(CustomerID)>2);
SELECT
c.FirstName
FROM
Customer c
INNER JOIN Purchases p
ON c.CustomerId = p.CustomerId
GROUP BY
c.FirstName
HAVING
SUM(p.Qty) > 2
While the IN suggestions would work they are kind of overkill and more than likely less performant than a straight up join with aggregation. The trick is the HAVING Clause by using it you can limit your result to the names you want. Here is a link to learn more about IN vs. Exists vs JOIN (NOT IN vs NOT EXISTS)
There are dozens of ways of doing this and to introduce you to Window Functions and common table expressions which are way over kill for this simplified example but are invaluable in your toolset as your queries continue to get more complex:
;WITH cte AS (
SELECT DISTINCT
c.FirstName
,SUM(p.Qty) OVER (PARTITION BY c.CustomerId) as SumOfQty
FROM
Customer c
INNER JOIN Purchases p
ON c.CustomerId = p.CustomerId
)
SELECT *
FROM
cte
WHERE
SumOfQty > 2

sql select count of multiple relationships with left join

I have a table for "branches", "orders" and "products. Each order and product are connected to a branch with branch_id. I need an sql statement to get a list of all branches with a field for how many orders and a field for how many products.
This works:
SELECT b.*, COUNT(o.id) AS orderCount FROM branches b
LEFT JOIN orders o ON (o.branch_id = b.id) GROUP BY b.id
but it only gets the amount of orders, not products.
If I change it to add amount of products, the amounts are wrong because it's getting amount of orders * amount of products.
How can I get the amount of both the orders and the products in the same SQL statement?
Something like this should work (on sql server at least - you didn't specify your engine).
SELECT
b.id
,COUNT(distinct o.id) AS orderCount
,COUNT(distinct p.id) AS productCount
FROM branches b
LEFT JOIN orders o
ON (o.branch_id = b.id)
left join products p
on p.product_id=b.id)
GROUP BY
b.id
Please try:
select
*,
(select COUNT(*) from Orders o where o.branch_id=b.id) OrderCount,
(select COUNT(*) from Products p where o.branch_id=p.id) ProductCount
From
branches b

SQL Query for counting number of orders per customer and Total Dollar amount

I have two tables
Order with columns:
OrderID,OrderDate,CID,EmployeeID
And OrderItem with columns:
OrderID,ItemID,Quantity,SalePrice
I need to return the CustomerID(CID), number of orders per customer, and each customers total amount for all orders.
So far I have two separate queries. One gives me the count of customer orders....
SELECT CID, Count(Order.OrderID) AS TotalOrders
FROM [Order]
Where CID = CID
GROUP BY CID
Order BY Count(Order.OrderID) DESC;
And the other gives me the total sales. I'm having trouble combining them...
SELECT CID, Sum(OrderItem.Quantity*OrderItem.SalePrice) AS TotalDollarAmount
FROM OrderItem, [Order]
WHERE OrderItem.OrderID = [Order].OrderID
GROUP BY CID
I'm doing this in Access 2010.
You would use COUNT(DISTINCT ...) in other SQL engines:
SELECT CID,
Count(DISTINCT O.OrderID) AS TotalOrders,
Sum(OI.Quantity*OI.SalePrice) AS TotalDollarAmount
FROM [Order] O
INNER JOIN [OrderItem] OI
ON O.OrderID = OI.OrderID
GROUP BY CID
Order BY Count(DISTINCT O.OrderID) DESC
Which Access unfortunately does not support. Instead you can first get the Order dollar amounts and then join them before figuring the order counts:
SELECT CID,
COUNT(Orders.OrderID) AS TotalOrders,
SUM(OrderAmounts.DollarAmount) AS TotalDollarAmount
FROM [Orders]
INNER JOIN (SELECT OrderID, Sum(Quantity*SalePrice) AS DollarAmount
FROM OrderItems GROUP BY OrderID) AS OrderAmounts
ON Orders.OrderID = OrderAmounts.OrderID
GROUP BY CID
ORDER BY Count(Orders.OrderID) DESC
If you need to include Customers that have orders with no items (unusual but possible), change INNER JOIN to LEFT OUTER JOIN.
Create a query which uses your 2 existing queries as subqueriers, and join the 2 subqueries on CID. Define your ORDER BY in the parent query instead of in a subquery.
SELECT
sub1.CID,
sub1.TotalOrders,
sub2.TotalDollarAmount
FROM
(
SELECT
CID,
Count(Order.OrderID) AS TotalOrders
FROM [Order]
GROUP BY CID
) AS sub1
INNER JOIN
(
SELECT
CID,
Sum(OrderItem.Quantity*OrderItem.SalePrice)
AS TotalDollarAmount
FROM OrderItem INNER JOIN [Order]
ON OrderItem.OrderID = [Order].OrderID
GROUP BY CID
) AS sub2
ON sub1.CID = sub2.CID
ORDER BY sub1.TotalOrders DESC;

question about SQL query

I'm working on a small project involving oracle database,
and I have the following tables:
CUSTOMER ( Cid, CName, City, Discount )
PRODUCT ( Pid, PName, City, Quantity, Price )
ORDERS ( OrderNo, Month, Cid, Aid, Pid, OrderedQuantity, Cost )
How can retrieve the names of all customers who ordered all the products?
For example if customer x ordered product1, product2 and product3 (which are all the products the company offers) he will be selected. And if customer y only ordered product 1 and 2 but not 3 he will not be selected.
How can I achieve this?
You want "relational division".
select *
from customer c
where not exists( -- There are no product
select 'x'
from product p
where not exists( -- the customer did not buy
select 'x'
from orders o
where o.cid = c.cid
and o.pid = p.id));
or
select c.cid
,c.name
from customer c
join orders o using(cid)
group
by c.id
,c.name
having count(distinct o.pid) = (select count(*) from product);
Here is a great article by Joe Celko that shows several ways of implementing relational division (and variations): Divided We Stand: The SQL of Relational Division
You can use group by and use a having clause to demand that the customer has ordered all products there are:
select c.CName
from Customers c
join Orders o
on o.Cid = c.Cid
group by
c.Cid
, c.CName
having count(distinct o.Pid) = (select count(*) from products)
IMHO more readable than the "relational divison" approach, but less efficient.

Repeated elements counting for Sql datatables queries

I have 2 tables: an Order table and an orderDetails table.
I have written a inner join:
SELECT Order.id
FROM Order
INNER JOIN orderDetails
ON Order.id=orderDetails.id
I have got the output as:
id
100
100
100
101
101
From the above data, I want the count of each record output as:
id count
100 3
101 2
How would I do this?
Select OrderId , Count(*) as [Count]
from OrderDetials
Group By OrderId
OrderId will be a foreing key column referencing Order.Id column of Order Table
If your orderDetails.id references Order.id column This will be the query.
Select id , Count(*) as [Count]
from OrderDetials
Group By id
SELECT o.id, COUNT(*)
FROM Order o
JOIN orderDetails od ON o.id=od.id
GROUP BY o.id
You need to use the COUNT aggregate and the GROUP BY clause.
SELECT Order.id, COUNT(DISTINCT orderDetails.id)
FROM Order
INNER JOIN orderDetails ON Order.id=orderDetails.orderId
GROUP BY Order.id
It also looks like you need o alter the join condition slightly.
Use Group by
SELECT Order.id, count(*) as Count
FROM Order
INNER JOIN orderDetails
ON Order.id=orderDetails.id Group by Order.id