How to select rows where multiple joined table values meet selection criteria? - sql

Given the following sample table schema
Customer Table
CustID
1
2
3
Invoice Table
CustID InvoiceID
1 10
1 20
1 30
2 10
2 20
3 10
3 30
The objective is to select all customers who have an InvoiceID value of 10 and 20 (not OR). So, in this example customers w/ CustID=1 and 2 would be returned.
How would you construct the SELECT statement?

Use:
SELECT c.custid
FROM CUSTOMER c
JOIN INVOICE i ON i.custid = c.custid
WHERE i.invoiceid IN (10, 20)
GROUP BY c.custid
HAVING COUNT(DISTINCT i.invoiceid) = 2
The key thing is that the counting of i.invoiceid needs to equal the number of arguments in the IN clause.
The use of COUNT(DISTINCT i.invoiceid) is in case there isn't a unique constraint on the combination of custid and invoiceid -- if there's no chance of duplicates you can omit the DISTINCT from the query:
SELECT c.custid
FROM CUSTOMER c
JOIN INVOICE i ON i.custid = c.custid
WHERE i.invoiceid IN (10, 20)
GROUP BY c.custid
HAVING COUNT(i.invoiceid) = 2

The Group By answers will work unless it is possible for there to be multiples of CustID/InvoiceId in the Invoice table. Then you might get some unexpected results. I like the answer below a little better because it mirrors more closely the logic as you are describing it.
Select CustID
From Customer
Where
Exists (Select 1 from Invoice Where CustID=Customer.CustID and InvoiceID=10)
and
Exists (Select 1 from Invoice Where CustID=Customer.CustID and InvoiceID=20)

select CustID
from InvoiceTable
where InvoiceID in (10,20)
group by CustID
having COUNT(distinct InvoiceID) = 2

Related

SQL Sumation from Different Tables

I would like some help on this matter.
I Have three tables.
Products Table,
Invoice Table,
Cash Table
Products Table has 2 Columns as follows (PID,BRAND)
Invoice Table has 2 columns as follows (PID,TOTALSALES)
Cash Table has 2 columns as follows (PID,TOTALSALES)
The PID's could have many different BRANDS.
for eg:
PID BRAND
1 TEST1
2 TEST2
3 TEST3
All the three tables are linked by the PID column.
i have many rows in the invoice and cash tables.
My problem is to Group the SUMMED values of the TOTALSALES in the INVOICE and CASH tables BRAND wise.
I tried using left joins but the summation value always increases when there are more than 2 table in the join.
Any help will be highly appreciated.
You can use union all and then aggregate the results from the two tables together:
select p.brand, sum(invoice_sales) as invoice_sales, sum(cash_sales) as cash_sales
from ((select pid, totalsales as invoice_sales, 0 as cash_sales
from invoice i
) union all
(select pid, 0, totalsales
from cash c
)
) ic join
product p
on ic.pid = p.id
group by p.brand;
summarise the data in the Invoice and Cash tables separately
SELECT
pr.BRAND, (ISNULL(Inv.TOTALSALES, 0.00) + ISNULL(Csh.TOTALSALES, 0.00)) TotalSales
FROM Products pr
LEFT JOIN (
SELECT
PID, SUM(TOTALSALES) TOTALSALES
FROM Invoice GROUP BY PID
) Inv ON Inv.PID = pr.PID
LEFT JOIN (
SELECT
PID, SUM(TOTALSALES) TOTALSALES
FROM Cash GROUP BY PID
) Csh ON Csh.PID = pr.PID
ORDER BY pr.BRAND
You need two separate sums for the results of joining the tables by PID values and you will need to group the records by BRAND
select sum(Invoice.TOTALSALES) as invoice_sales, sum(Cash.TOTALSALES) as cash_sales
from Products
join Invoice
on Products.PID = Invoice.PID
join Cash
on Products.PID = Cash.PID
group by Invoice.PID, Products.BRAND;
If you do not need to group by products, then just omit Invoice.PID from the group by.
another way to do it is by using subquerys
select p.*,
(select sum(i.TOTALSALES) from Invoice i where i.PID = p.PID) as TotalSalesInvoice,
(select sum(c.TOTALSALES) from Cash c where c.PID = p.PID) as TotalSalesCash
from Products p

How to count sub data of customer in SQL

I have Customer table as following
CustomerId CustomerName CustomerEmail
1 AAA AAA#AAA.com
2 BBB BBB#BBB.BBB
This is example data I have shown. Like this I have 50000 customer in customer table now their invoices are also stored in invoice table
InvoiceId InvoiceAmount InvoiceCustomerID
1 22.00 1
2 58.00 2
3 21.00 2
4 45.00 2
Now I want to count the invoice for each customer when I show the customer listing, for example:
CustomerId CustomerName CustomerEmail TotalInvoice
1 AAA AAA#AAA.com 1
2 BBB BBB#BBB.BBB 3
I have used the following query but didnt worked
SELECT customer.*,COUNT(invoice.InvoiceId) FROM `customer` as customer INNER JOIN invoice as invoice ON customer.CustomerId= invoice.InvoiceCustomerID
Can suggest any method to solve this ?
Also there is more then 5 lacks invoice so it's taking to much time to load.
SELECT a.CustomerID, a.CustomerName, a.CustomerEmail,
SUM(CASE WHEN b.InvoiceID IS NOT NULL THEN 1 ELSE 0 END) as TotalInvoice
FROM `customer` a LEFT JOIN invoice b ON a.CustomerId= b.InvoiceCustomerID
GROUP BY a.CustomerId, a.CustomerName, a.CustomerEmail
Just use GROUP BY
Also, do you need to display customers with no invoices? You're going to have to turn this into a left join if you do.
You can't use a grouping function without GROUP BY:
SELECT customer.CustomerID, customer.Customer.Name, customer.CustomerEmail
,COUNT(invoice.InvoiceId) AS RC
FROM `customer` AS customer
INNER JOIN invoice AS invoice ON customer.CustomerId = invoice.InvoiceCustomerID
GROUP BY customer.CustomerID, customer.Customer.Name, customer.CustomerEmail
select c.CustomerId "CustomerId",
c.CustomerName "CustomerName",
c.CustomerEmail "CustomerEmail",
count(i.InvoiceCustomerID) "TotalInvoice"
from customer as c
inner join invocie i
on(i.InvoiceCustomerID = c.CustomerId) group by
c.CustomerId, c.CustomerName, c.CustomerEmail order by CustomerId
due to bulk data other queries was taking to much time to load
so I have settled the issue using following query
SELECT c.CustomerId,c.CustomerName,totalinvoice AS ttlinv FROM customer
as c LEFT JOIN (select COUNT(InvoiceId) as totalinvoice,InvoiceCustomerID
from invoice GROUP by InvoiceCustomerID) as io on io.InvoiceCustomerID=c.CustomerId

How to get the count and display only one row

I've got 3 Tables:
tblCustomer
CustomerID CustomerName
1 Customer 1
2 Customer 2
tblOrder
OrderID CustomerID OrderTypeID LoanNumber
1 1 1 98513542
2 1 1 71283527
3 1 1 10268541
4 1 1 61258965
tblOrderType
OrderTypeID OrderTypeName
1 Purchase
2 Rental
Now, I'm looking to return CustomerID, CustomerName, OrderTypeName and OrderCount, where OrderCount is how many of each order the customer has. I'm using the following query:
SELECT tblCustomer.CustomerID, tblCustomer.customerName, tblOrderType.OrderTypeName, tblOrder.OrderID
FROM tblCustomer
INNER JOIN tblOrder
ON tblCustomer.CustomerID = tblOrder.CustomerID
INNER JOIN tblOrderType
ON tblOrderType.OrderTypeID = tblOrder.OrderTypeID
It sort of works. It gets all I'm asking for, except for the OrderCount, obviously. The result is like this:
CustomerID CustomerName OrderTypeName OrderID
1 Customer 1 Purchase 1
1 Customer 1 Purchase 2
1 Customer 1 Purchase 3
1 Customer 1 Purchase 4
But what I'm looking for is this:
CustomerID CustomerName OrderTypeName OrderCount
1 Customer 1 Purchase 4
Now, I've tried adding Count() into the query at various places (Count(tblOrder.OrderID) for one), and I get an error that tblCustomer.CustomerID is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
This isn't a homework assignment. I just don't know a whole lot about sql and database interactions, since it wasn't taught in my school, and I've got a friend who's throwing scenarios at me.
You need to use grouping and aggregation:
SELECT tblCustomer.CustomerID, tblCustomer.customerName, tblOrderType.OrderTypeName,
count (tblOrder.OrderID) asOrderCOunt
FROM tblCustomer
INNER JOIN tblOrder
ON tblCustomer.CustomerID = tblOrder.CustomerID
INNER JOIN tblOrderType
ON tblOrderType.OrderTypeID = tblOrder.OrderTypeID
GROUP BY
tblCustomer.CustomerID,
tblCustomer.customerName,
tblOrderType.OrderTypeName
You can't use aggregate functions without grouping everything that isn't aggregated.
The error message does tell you the issue. Whenever you use an aggregation operator (COUNT, SUM, AVG, etc.), you have to tell the database how these should be aggregated together. You do this with a GROUP BY statement (the link there is for SQL Server, but it should generally apply to all database engines):
SELECT
C.CustomerID
,C.customerName
,OT.OrderTypeName
,COUNT(O.OrderID)
FROM tblCustomer C
INNER JOIN tblOrder O
ON C.CustomerID = O.CustomerID
INNER JOIN tblOrderType OT
ON OT.OrderTypeID = O.OrderTypeID
GROUP BY
C.CustomerID
,C.customerName
,OT.OrderTypeName
Try this, just add group by and count the grouped rows
SELECT tblCustomer.CustomerID, tblCustomer.customerName,
tblOrderType.OrderTypeName, tblOrder.OrderID,
COUNT(tblOrder.OrderID) as OrderCount
FROM tblCustomer INNER JOIN tblOrder
ON tblCustomer.CustomerID = tblOrder.CustomerID
INNER JOIN tblOrderType
ON tblOrderType.OrderTypeID = tblOrder.OrderTypeID
GROUP BY tblCustomer.CustomerID, tblCustomer.customerName,
tblOrderType.OrderTypeName

Segment purchases based on new vs returning

I'm trying to write a query that can select a particular date and count how many of those customers have placed orders previously and how many are new. For simplicity, here is the table layout:
id (auto) | cust_id | purchase_date
-----------------------------------
1 | 1 | 2010-11-15
2 | 2 | 2010-11-15
3 | 3 | 2010-11-14
4 | 1 | 2010-11-13
5 | 3 | 2010-11-12
I was trying to select orders by a date and then join any previous orders on the same user_id from previous dates, then count how many had orders, vs how many didnt. This was my failed attempt:
SELECT SUM(
CASE WHEN id IS NULL
THEN 1
ELSE 0
END ) AS new, SUM(
CASE WHEN id IS NOT NULL
THEN 1
ELSE 0
END ) AS returning
FROM (
SELECT o1 . *
FROM orders AS o
LEFT JOIN orders AS o1 ON ( o1.user_id = o.user_id
AND DATE( o1.created ) = "2010-11-15" )
WHERE DATE( o.created ) < "2010-11-15"
GROUP BY o.user_id
) AS t
Given a reference data (2010-11-15), then we are interested in the number of distinct customers who placed an order on that date (A), and we are interested in how many of those have placed an order previously (B), and how many did not (C). And clearly, A = B + C.
Q1: Count of orders placed on reference date
SELECT COUNT(DISTINCT Cust_ID)
FROM Orders
WHERE Purchase_Date = '2010-11-15';
Q2: List of customers placing order on reference date
SELECT DISTINCT Cust_ID
FROM Orders
WHERE Purchase_Date = '2010-11-15';
Q3: List of customers who placed an order on reference date who had ordered before
SELECT DISTINCT o1.Cust_ID
FROM Orders AS o1
JOIN (SELECT DISTINCT o2.Cust_ID
FROM Orders AS o2
WHERE o2.Purchase_Date = '2010-11-15') AS c1
ON o1.Cust_ID = c1.Cust_ID
WHERE o1.Purchase_Date < '2010-11-15';
Q4: Count of customers who placed an order on reference data who had ordered before
SELECT COUNT(DISTINCT o1.Cust_ID)
FROM Orders AS o1
JOIN (SELECT DISTINCT o2.Cust_ID
FROM Orders AS o2
WHERE o2.Purchase_Date = '2010-11-15') AS c1
ON o1.Cust_ID = c1.Cust_ID
WHERE o1.Purchase_Date < '2010-11-15';
Q5: Combining Q1 and Q4
There are several ways to do the combining. One is to use Q1 and Q4 as (complicated) expressions in the select-list; another is to use them as tables in the FROM clause which don't need a join between them because each is a single-row, single-column table that can be joined in a Cartesian product. Another would be a UNION, where each row is tagged with what it calculates.
SELECT (SELECT COUNT(DISTINCT Cust_ID)
FROM Orders
WHERE Purchase_Date = '2010-11-15') AS Total_Customers,
(SELECT COUNT(DISTINCT o1.Cust_ID)
FROM Orders AS o1
JOIN (SELECT DISTINCT o2.Cust_ID
FROM Orders AS o2
WHERE o2.Purchase_Date = '2010-11-15') AS c1
ON o1.Cust_ID = c1.Cust_ID
WHERE o1.Purchase_Date < '2010-11-15') AS Returning_Customers
FROM Dual;
(I'm blithely assuming MySQL has a DUAL table - similar to Oracle's. If not, it is trivial to create a table with a single column containing a single row of data. Update 2: bashing the MySQL 5.5 Manual shows that 'FROM Dual' is supported but not needed; MySQL is happy without a FROM clause.)
Update 1: added qualifier 'o1.Cust_ID' in key locations to avoid 'ambiguous column name' as indicated in the comment.
How about
SELECT * FROM
(SELECT * FROM
(SELECT CUST_ID, COUNT(*) AS ORDER_COUNT, 1 AS OLD_CUSTOMER, 0 AS NEW_CUSTOMER
FROM ORDERS
GROUP BY CUST_ID
HAVING ORDER_COUNT > 1)
UNION ALL
(SELECT CUST_ID, COUNT(*) AS ORDER_COUNT, 0 AS OLD_CUSTOMER, 1 AS NEW_CUSTOMER
FROM ORDERS
GROUP BY CUST_ID
HAVING ORDER_COUNT = 1)) G
INNER JOIN
(SELECT CUST_ID, ORDER_DATE
FROM ORDERS) O
USING (CUST_ID)
WHERE ORDER_DATE = [date of interest] AND
OLD_CUSTOMER = [0 or 1, depending on what you want] AND
NEW_CUSTOMER = [0 or 1, depending on what you want]
Not sure if that'll do the whole thing, but it might provide a starting point.
Share and enjoy.
select count(distinct o1.cust_id) as repeat_count,
count(distinct o.cust_id)-count(distinct o1.cust_id) as new_count
from orders o
left join (select cust_id
from orders
where purchase_date < "2010-11-15"
group by cust_id) o1
on o.cust_id = o1.cust_id
where o.purchase_date = "2010-11-15"

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