How to use subquery in WHERE? (AdventureWorks) - sql

What am I doing wrong? I need to make sure my column 'OrderCount' only shows values more than 20. Please note: I am not allowed to use JOIN in this case.
SELECT FirstName, LastName, (SELECT COUNT(SalesOrderID)
FROM SalesOrderHeader
WHERE SalesOrderHeader.ContactID = Contact.ContactID) AS OrderCount
FROM Contact
WHERE COUNT(SalesOrderID) = (SELECT COUNT(SalesOrderID)
FROM SalesOrderHeader
WHERE COUNT(SalesOrderID) > 20

Basically, you would need to correlate the subquery in the where clause as well - yours count all orders, not those of the concerned customers only.
But a subquery seems simpler:
SELECT *
FROM (
SELECT c.FirstName, c.LastName,
(
SELECT COUNT(s.SalesOrderID)
FROM SalesOrderHeader s
WHERE s.ContactID = c.ContactID
) AS OrderCount
FROM Contact c
) t
where OrderCount > 20
Please note: I am not allowed to use JOIN in this case.
This really is a contrived example. In real life situation, a join with outer aggregation and a having clause, or a lateral join, would be more appropriate:
SELECT c.FirstName, c.LastName, o.OrderCount
FROM Contact c
CROSS APPLY (
SELECT COUNT(s.SalesOrderID) as OrderCount
FROM SalesOrderHeader s
WHERE s.ContactID = c.ContactID
) AS o
where o.OrderCount > 20

SELECT FirstName, LastName, (SELECT COUNT(SalesOrderID)
FROM SalesOrderHeader
WHERE SalesOrderHeader.ContactID = Contact.ContactID) AS OrderCount
FROM Contact
WHERE Contact.ContactID in (SELECT
SalesOrderHeader.ContactID
FROM SalesOrderHeader
Group by
SalesOrderHeader.ContactID
Having COUNT(*) > 20

The problem here isn't the subquery, it's the aggregate function (the COUNT in this case) that's causeing the problem. You can't put an aggregate function that references the tables in the FROM in the WHERE you would need to reference it in the HAVING.
I think you can also condense this right down to not need a subquery:
SELECT C.FirstName,
C.LastName
FROM dbo.Contact C
JOIN dbo.SalesOrderHeader SOH ON C.ContactID = SOH.ContactID
GROUP BY C.FirstName,
C.LastName
HAVING COUNT(SalesOrderID) > 20;

Related

show each customers' names, how much each person has spent in total, and how many orders they have made

enter image description here
I am trying to extract infomation from atable to solve the aforementioned question:
show each customers' names, how much each person has spent in total, and how many orders they have made
Here is my attempt
select firstName, lastName, ISNULL(totalOrders, 0), ISNULL(totalSpent) from Customers as C
join (
SELECT customerID, count(orderNumber) as totalOrders from CustomerOrders
ORDER BY COUNT(orderNumber) ASC
) AS CO ON CO.customerID = C.customerID
JOIN (
select sum(orderNumber) as totalSpent from ItemsInOrder
order by C.lastName
) as IIO ON IIO.totalSpent = CO.totalOrders
Unfortunately, this did not run. Also i'm trying to get my result to be ordered by order count in ascending and order by the customer's last name but I'm having a hard time, as I don't know where to place it.
I feel like this is an easy question but I kept overthinking it and ending up being confused
As #HoneyBadger has pointed out in the comments, you are not using any group by clause, so that is going to error out. But, you have to look at how you join your tables as well. It does not make sense to equate the sum of item costs to the total number of orders. You should be joining that on order number.
Here is a quick and dirty answer using outer apply. Because count ignores nulls in the count on a single column, we don't need an isnull or a coalesce there, but we would on the sum column.
select
c.firstname,
c.lastname,
sum(coalesce(c.totalspend, 0)) as TotalSpend,
count(a.orderNumber) as Numorders
from customers c
outer apply
(
select co.customerid, orderNumber, sum(totalItemCost) as TotalSpend
from customerorders co
left join itemsinorders ii on ii.ordernumber = co.ordernumber
group by co.customerid, ordernumber
) a on a.customerid = c.customerid
Group by c.firstname, c.lastname
If outer apply doesn't work, you should be able to do it all with joins with a count(distinct). So,
select
c.firstname,
c.lastname,
count(distinct o.ordernumber) as NumOrders,
sum(coalesce(i.totalspend, 0) as totalSpend
from customers c
left join customerorders o on o.customerid = c.customerid
left join itemsinorders i on i.orderid = o.orderid
group by c.firstname, c.lastname
Without being able to check on the null values of distinct, this should work and is easier to read.

I need a solution to this SQL Query I'm trying to solve

"Write a query that determines the customer that has spent the most on
music for each country. Write a query that returns the country along
with the top customer and how much they spent. For countries where the
top amount spent is shared, provide all customers who spent this
amount.
You should only need to use the Customer and Invoice tables.
Check Your Solution
Though there are only 24 countries, your query should return 25 rows
because the United Kingdom has 2 customers that share the maximum."
You can find the data set here
.
Here is the code I tried with the results
And here is the expected outcome
Generally, you should always GROUP BY anything in your SELECT that is not an aggregation function (e.g. SUM). Try this:
SELECT c.CustomerId, c.FirstName, c.LastName, c.Country,
SUM(i.Total) AS TotalSpent
FROM Customer c
JOIN Invoice i
ON i.CustomerId = c.CustomerId
GROUP BY c.CustomerId, c.FirstName, c.LastName, c.Country
ORDER BY c.Country
WITH tab1 AS ( SELECT c.CustomerId, c.FirstName, c.LastName, c.Country, SUM(i.Total) TotalSpent FROM Customer c JOIN Invoice i ON c.CustomerId = i.CustomerId GROUP BY c.CustomerId ) SELECT tab1.* FROM tab1 left JOIN ( SELECT CustomerId, FirstName, LastName, Country, MAX(TotalSpent) AS TotalSpent FROM tab1 GROUP BY Country ) tab2 ON tab1.Country = tab2.Country WHERE tab1.TotalSpent = tab2.TotalSpent ORDER BY Country;

SQL TOP 1 Syntax for a nested query

New to SQL Server and I am trying to use top 1 to get the company with the most order in my DB within my code that is already working but I don't know how to use it properly. Only missing syntax I think.
Query #1 is working fine:
SELECT
c.CompanyName, COUNT(DISTINCT OrderID) as Nombre_Commande
FROM
Orders O
INNER JOIN
Customers C ON O.CustomerID = c.CustomerID
GROUP BY
c.CompanyName
What I am trying to do
SELECT TOP (1) *
FROM
(SELECT
c.CompanyName, COUNT(DISTINCT OrderID) AS Nombre_Commande
FROM
Orders O
INNER JOIN
Customers C ON O.CustomerID = c.CustomerID
GROUP BY
c.CompanyName)
You need to give the derived table an alias, and also, specifying top without an order by clause is pretty pointless as rows are returned as a set without any order unless the order is explicitly specified with an order by clause:
SELECT TOP (1) *
FROM (
SELECT c.CompanyName, COUNT(DISTINCT OrderID) as Nombre_Commande
FROM Orders O
INNER JOIN Customers C ON O.CustomerID=c.CustomerID
GROUP by c.CompanyName
) AS YourTable
ORDER BY something_meaningful_maybe_nombre_commande?
How about this?
SELECT TOP 1 c.CompanyName, COUNT(DISTINCT OrderID) as Nombre_Commande
FROM Orders O INNER JOIN
Customers C
ON O.CustomerID = c.CustomerID
GROUP by c.CompanyName
ORDER BY Nombre_Commande DESC;
This assumes that Nombre_Commande is what you want to order by.
By the way, I would be surprised if COUNT(DISTINCT) were really needed for this query. COUNT(*) or COUNT(OrderId) should be sufficient.

Pulling my hair with this Syntax Error

I seem to be running into a query syntax error but cannot seem to isolate it. I am using MS Access and when I run the queries I get a syntax error in FROM clause.
I have two tables and they are in a one to many relationship:
Table 1 called (customer) with the following fields:
ID
FirstName
Table 2 called (tblservice) with the following fields:
serviceID
Timing
Total
customerID <-Foreign Key
First Query:
select c.id, c.firstname, avg(s.Total) / (select count(id) from customer) as LifetimeValue
from tblservice as s join customer as c on s.id = c.id
group by s.id
Second Query(30 day span):
select c.id, c.firstname, avg(s.Total) / (select count(id) from customer) as LifetimeValue
from tblservice as s join customer as c on s.id = c.id
where (s.Timing)>=DateAdd("d",-30,Date())
group by s.id
Try this:
select c.id, c.firstname, avg(s.Total) / count(c.id) as LifetimeValue
from tblservice as s inner join customer as c on s.id = c.id
group by c.id, c.firstname
and
select c.id, c.firstname, avg(s.Total) / count(c.id) as LifetimeValue
from tblservice as s inner join customer as c on s.id = c.id
where (s.Timing)>=DateAdd("d",-30,Date())
group by c.id, c.firstname
You cannot select c.id and c.firstname unless you group by them. And you can use count(c.id) since you are already grouping by c.id. I have not used SQL in MS Access though. So I am not 100% sure. Try it out.
MS Access may require you to use INNER JOIN instead of just JOIN.

Need hints on seemingly simple SQL query

I'm trying to do something like:
SELECT c.id, c.name, COUNT(orders.id)
FROM customers c
JOIN orders o ON o.customerId = c.id
However, SQL will not allow the COUNT function. The error given at execution is that c.Id is not valid in the select list because it isn't in the group by clause or isn't aggregated.
I think I know the problem, COUNT just counts all the rows in the orders table. How can I make a count for each customer?
EDIT
Full query, but it's in dutch... This is what I tried:
select k.ID,
Naam,
Voornaam,
Adres,
Postcode,
Gemeente,
Land,
Emailadres,
Telefoonnummer,
count(*) over (partition by k.id) as 'Aantal bestellingen',
Kredietbedrag,
Gebruikersnaam,
k.LeverAdres,
k.LeverPostnummer,
k.LeverGemeente,
k.LeverLand
from klanten k
join bestellingen on bestellingen.klantId = k.id
No errors but no results either..
When using an aggregate function like that, you need to group by any columns that aren't aggregates:
SELECT c.id, c.name, COUNT(orders.id)
FROM customers c
JOIN orders o ON o.customerId = c.id
GROUP BY c.id, c.name
If you really want to be able to select all of the columns in Customers without specifying the names (please read this blog post in full for reasons to avoid this, and easy workarounds), then you can do this lazy shorthand instead:
;WITH o AS
(
SELECT CustomerID, CustomerCount = COUNT(*)
FROM dbo.Orders GROUP BY CustomerID
)
SELECT c.*, o.OrderCount
FROM dbo.Customers AS c
INNER JOIN dbo.Orders AS o
ON c.id = o.CustomerID;
EDIT for your real query
SELECT
k.ID,
k.Naam,
k.Voornaam,
k.Adres,
k.Postcode,
k.Gemeente,
k.Land,
k.Emailadres,
k.Telefoonnummer,
[Aantal bestellingen] = o.klantCount,
k.Kredietbedrag,
k.Gebruikersnaam,
k.LeverAdres,
k.LeverPostnummer,
k.LeverGemeente,
k.LeverLand
FROM klanten AS k
INNER JOIN
(
SELECT klantId, klanCount = COUNT(*)
FROM dbo.bestellingen
GROUP BY klantId
) AS o
ON k.id = o.klantId;
I think this solution is much cleaner than grouping by all of the columns. Grouping on the orders table first and then joining once to each customer row is likely to be much more efficient than joining first and then grouping.
The following will count the orders per customer without the need to group the overall query by customer.id. But this also means that for customers with more than one order, that count will repeated for each order.
SELECT c.id, c.name, COUNT(orders.id) over (partition by c.id)
FROM customers c
JOIN orders ON o.customerId = c.id