SQL COUNT function to find commonality - sql

The question I'm facing is as follows: list the first letters of the product names and their totals. Only display the letter and count if there are 3 or more products beginning with that letter of the alphabet.
Clearly the query requires the use of the COUNT aggregate, but I am spinning my wheels looking at this. How do I write this query?
expected output:
ProductName Total
C 9
G 11
ETC...
I'm assuming I need a SUBSTRING in my select statement
SELECT SUBSTRING(ProductName,1,1) AS ProductName,
COUNT(ProductName) AS Total
FROM Products
But using WHERE with a count function will give me an aggregate error is SQLServer2012?

You cannot use aggregate function's in where clause to filter the result
To filter the group's you have to use having clause instead of where clause. Try this
SELECT SUBSTRING(ProductName,1,1) AS ProductName,
COUNT(ProductName) AS Total
FROM Products
group by SUBSTRING(ProductName,1,1)
having COUNT(ProductName) >=3

You just need a proper group and having clause:
SELECT
LEFT(ProductName, 1) AS ProductName,
COUNT(*) AS [Total]
FROM Products
GROUP BY
LEFT(ProductName, 1)
HAVING COUNT(*) >= 3

Related

SQL - select within select in query

In a very standard question of selecting all item names and their prices for those prices greater than the average price.
The query using having has the form:
'''SELECT Name, Price FROM items
GROUP BY Name
HAVING Price >
(SELECT AVG(Price) FROM items )
;'''
It seems that only when AVG is used in a SELECT statement then it is returning the column average. Else if I use HAVING Price > AVG(Price) nothing is returned. Why is an additional SELECT statement required, instead of just using AVG on Price?
That's just how GROUP BY works. In the SELECT part of the query, you can only have the columns that are used in the GROUP BY clause, or the aggregates, like MIN, MAX, AVG, SUM, etc. In this case, what's your aggregate of the column Price that you want to output? you should specify that.
Alternatively, you could use a windowed function for calculating the average price for each name unit, and then add the subquery to filter on that:
SELECT *
FROM (SELECT Name, Price, AVG(Price) OVER (PARTITION BY Name) AS Average_Price
FROM items) AS X
WHERE X.Price > X.Average_Price
I think your best option is a simple subquery in WHERE condition
select i.*
from items i
where Price > (
select avg(Price) avgPrice
from items ii
--where ii.Name = i.Name /* add this line if you need average to be calculated per Name */
)

Using Max and Sum in the same query

I'm trying to get the the best selling product in all sales, and I'm using the following query:
SELECT Max(Sum(sold_mount)),
product_code
FROM sold_ items
GROUP BY product_code;
and it's returning "not a single-group group function"
it's possible to use max and sum in the same query?
I would recommend writing this as:
SELECT SUM(sold_mount), product_code
FROM sold_ items
GROUP BY product_code
ORDER BY SUM(sold_amount) DESC
FETCH FIRST 1 ROW ONLY;

Creating variable in SQL and using in WHERE clause

I want to create a variable that counts the number of times each customer ID appears in the CSV, and then I want the output to be all customer IDs that appear 0,1,or 2 times. Here is my code so far:
SELECT Customers.customer_id , COUNT(*) AS counting
FROM Customers
LEFT JOIN Shopping_cart ON Customers.customer_id = Shopping_cart.customer_id
WHERE counting = '0'
OR counting = '1'
OR counting = '2'
GROUP BY Customers.customer_id;
SELECT Customers.customer_id , COUNT(*) AS counting
FROM Customers LEFT JOIN Shopping_cart on Customers.customer_id=Shopping_cart.customer_id
WHERE COUNT(*) < 3
GROUP BY Customers.customer_id;
The query groups all customer ids, and with count() we get the number of items in a group. So for your solution you call the group count() and say only the items where the group count is smaller then 3. Smaller then 3 includes (0,1,2). You can reuse the count() in the query.
You're probably thinking of HAVING, not WHERE.
For example:
select JOB, COUNT(JOB) from SCOTT.EMP
group by JOB
HAVING count(JOB) > 1 ;
While a tad odd, you may be specific about the HAVING condition(s):
HAVING count(JOB) = 2 or count(JOB) = 4
Note: the WHERE clause is used for filtering rows and it applies on each and every row, while the HAVING clause is used to filter groups.
You can apply a filter after the aggregation with the HAVING clause.
Please note that count(*) counts all rows, including empty ones, so you cannot use it to detect customers without any shopping cart; you have to count the non-NULL values in some column instead:
SELECT customer_id,
count(Shopping_cart.some_id) AS counting
FROM Customers
LEFT JOIN Shopping_cart USING (customer_id)
GROUP BY customer_id
HAVING count(Shopping_cart.some_id) BETWEEN 0 and 2;

Summary Query to Sum Operations not working

SELECT price,total_price,SUM(mount) As TotalAmount
FROM sales
group by [date]
is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
You cannot select fields in the SELECT part which is not a part of an Aggregated function or specified in the the GROUP BY clause when using GROUP BY.
You can select "mount" because it's aggregated (SUM), but not price or total_price because they're not aggregated nor part of your group by date
You will need to either change your group by functionality, or the select part.
You have to group by the remaining tables that are not a function, therefore:
SELECT price,total_price,SUM(mount) As TotalAmount
FROM sales
Group by price,total_price
Order by date

How do I select those records where the group by clause returns 2 or more?

I'd like to return a list of items of only those that have two or more in the group:
select count(item_id) from items group by type_id;
Specifically, I'd like to know the values of item_id when the count(item_id) == 2.
You're asking for something that's not particularly possible without a subquery.
Basically, you want to list all values in a column while aggregating on that same column. You can't do this. Aggregating on a column makes it impossible to list of all the individual values from that column.
What you can do is find all type_id values which have an item_id count equal to 2, then select all item_ids from records matching those type_id values:
SELECT item_id
FROM items
WHERE type_id IN (
SELECT type_id
FROM items
GROUP BY type_id
HAVING COUNT(item_id) = 2
)
This is best expressed using a join rather than a WHERE IN clause, but the idea is the same no matter how you approach it. You may also want to select distinct item_ids in which case you'll need the DISTINCT keyword before item_id in the outer query.
If your SQL dialect includes GROUP_CONCAT(), that could be used to generate a list of items without the inner query. However, the results differ; the inner query returns one item id per row, where GROUP_CONCAT() returns multiple ids as a string.
SELECT type_id, GROUP_CONCAT(item_id), COUNT(item_id) as number
FROM items
GROUP BY type_id
HAVING number = 2
Try this sql query:
select count(item_id) from items group by type_id having count(item_id)=2;