SQL query can't use variable in FROM statement - sql

I'm new to SQL, so sorry for maybe stupid question.
Table will be from this SQL sandbox:
https://www.w3schools.com/sql/trysql.asp?filename=trysql_asc
There is table of format
OrderDetailID OrderID ProductID Quantity
1 10248 11 12
2 10248 42 10
3 10248 72 5
4 10249 14 9
5 10249 51 40
I want to get products with maximum average quantity.
I can get this using the following query:
SELECT avg.ProductID, avg.Quantity
FROM (
SELECT ProductID, AVG(Quantity) Quantity
FROM OrderDetails
GROUP BY ProductID
) avg
WHERE avg.Quantity = (
SELECT MAX(Quantity) FROM (
SELECT ProductID, AVG(Quantity) Quantity
FROM OrderDetails
GROUP BY ProductID
)
)
ProductID Quantity
8 70
48 70
Here I twice use block
SELECT ProductID, AVG(Quantity) Quantity
FROM OrderDetails
GROUP BY ProductID
because if I use query with avg instead of second block
SELECT avg.ProductID, avg.Quantity
FROM (
SELECT ProductID, AVG(Quantity) Quantity
FROM OrderDetails
GROUP BY ProductID
) avg
WHERE avg.Quantity = (SELECT MAX(Quantity) FROM avg)
I get error could not prepare statement (1 no such table: avg)
So my question is:
Is it a kind of syntaxis mistake and could be simply corrected, or for some reason I can't use variables like that?
Is there simplier way to make the query I need?

Consider Common Table Expressions (CTE) using WITH clause which allows you to avoid repeating and re-calculating the aggregate subquery. Most RDBMS's supports CTEs (fully valid in your SQL TryIt linked page).
WITH avg AS (
SELECT ProductID, AVG(Quantity) Quantity
FROM OrderDetails
GROUP BY ProductID
)
SELECT avg.ProductID, avg.Quantity
FROM avg
WHERE avg.Quantity = (
SELECT MAX(Quantity) FROM avg
)

This is not really a syntax thing, this is rather scope: you try to
reference an alias where it is not in a parent-child relationship. Only this way they can reference each other. (The identifier there is an alias not a variable - that's a different thing.)
A simpler way is to create a temporary set before you run the filter condition - as in a previous answer, with a CTE, or you can try with a temp table. These can be used anywhere because their scope is not within a subquery.

Related

I'm trying to find the grouped category's that have a average greater than x

The problem is that when I run my query I receive the incorrect Average math back from what I get on a calculator when using the greater than statement.
What's happening is when the average is occurring it is only using values above the where clause value. What I want to happen is for it to GROUP the category's get the average of all the prices in that category and then only show me the ones above the value.
This version works because it is looking directly at the category without the greater than question.
SELECT CategoryID, AVG(Price)
FROM Products
WHERE CategoryID="3"
GROUP BY CategoryID
Returned Data
CategoryID AVG(Price)
3 25.16
This is the version I'm having trouble with.
SELECT CategoryID, AVG(Price)
FROM Products
WHERE Price > 30
GROUP BY CategoryID;
Returned Data
CategoryID AVG(Price)
1 154.75
2 41.95
3 51.3575
4 38.300000000000004
5 35.625
6 73.14750000000001
7 49.3
8 46.75
use having
SELECT CategoryID, AVG(Price)
FROM Products
GROUP BY CategoryID
having AVG(Price)>x
having is a group filter , so here you need having instead of where
You want having - using where is just giving you the averages of prices that are over 30.
So:
SELECT CategoryID, AVG(Price)
FROM Products
GROUP BY CategoryID
having AVG(Price) > 30
Try this-
SELECT CategoryID, AVG(Price)
FROM Products
--WHERE Price > 30
GROUP BY CategoryID;
HAVING AVG(Price) > 30
Usually you use the HAVING keyword to select from groups formed by GROUP BY. In this HAVINGclause you can use all the different criteria to select from the groups.
SELECT CategoryID, AVG(Price)
FROM Products
GROUP BY CategoryID
HAVING AVG(Price) > x
You need to use HAVING in this case. HAVING allows you to filter on aggregated (like an average) fields, while WHERE limits you to only the actual values in the data at rest. Try this:
SELECT CategoryID, AVG(Price)
FROM Products
GROUP BY CategoryID
HAVING AVG(Price) > 30
Here is a good tutorial on HAVING vs WHERE

SQL query MAX() function

select ProductID,ProductName,QuantityPerUnit
from Products
where Products.UnitPrice=Max(UnitPrice)
What is wrong in this query?
I had tried 'having' in place of 'where' tag.
select ProductID,ProductName,QuantityPerUnit
from Products
having Products.UnitPrice=Max(UnitPrice)
it must produce result with max unit price,
ProductID ProductName QuantityPerUnit
38 Côte de Blaye 12 - 75 cl bottles
If you want the product(s) with the maximum UnitPrice:
select ProductID,ProductName,QuantityPerUnit
from Products
where UnitPrice = (select Max(UnitPrice) from Products)
This query:
select Max(UnitPrice) from Products
returns the maximum UnitPrice.
If there are more than 1 products with that price, they will all be returned.
If you want just 1, you can use LIMIT 1 or TOP 1, depending on the rdbms you use.
You cannot simply use aggregate function in a where clause unless it's in a subquery.
Try :::
select ProductID,ProductName,QuantityPerUnit from Products
Where Products.UnitPrice= (select Max(UnitPrice) from Products)
If you're using TSQL, then better use
select top 1 With ties ProductID,ProductName,QuantityPerUnit from Products
Order by UnitPrice desc
This will give you all the products with highest unit price and is efficient since there's no subquery.
Use sub query to find max price
SELECT ProductID, ProductName, QuantityPerUnit
FROM Products
WHERE UnitPrice = (SELECT Max(UnitPrice) FROM Products)
can use HAVING clause
SELECT ProductID, ProductName, QuantityPerUnit FROM Products
GROUP BY ProductID, ProductName, QuantityPerUnit
HAVING UnitPrice = MAX(UnitPrice)
What is wrong with your query is that you are mixing aggregation value with raw values in the query.
If you want only one row, I would recommend a different approach:
select ProductID, ProductName, QuantityPerUnit
from Products
order by UnitPrice desc
fetch first 1 row only;
Although fetch first is standard SQL, not all databases support it, so you may need the appropriate syntax for your database.
What I tried is:
Select Max(UnitPrice) From Products
select Max(UnitPrice) ProductID,ProductName,QuantityPerUnit
from Products
Group By ProductID,ProductName,QuantityPerUnit having Max(UnitPrice)>200

Query to See Count of ProductName against same ProductId

How to write Sql Query to show Number of Names based on one Product Id.
For example in this example: For ProductId - 263, Count(Name) = 3
I would like to see
263 3
264 2
265 10
266 0 (if null)
SELECT productid, COUNT(*)
FROM products
GROUP BY productid
It's not an exact answer, but it will return the number of occurrences of the productid for each unique productid. It may help you find your result.
Assuming you have a table of products, then you want a left join:
select p.productid, count(pn.productid)
from products p left join
productnames pn
on p.productid = pn.productid
group by p.productid;
Pang.
I have written an answer using a CTE that will give you the count of all the names for each product that can easily be changed to COUNT the DISTINCT names.
;WITH CTE_DISTINCT_NAMES
AS (SELECT --DISTINCT
/* Uncomment "DISTINCT" above
to include duplicate names
in the COUNT
*/
ProductId
, Name
FROM TABLE1
)
SELECT T.ProductId
, COUNT(ISNULL(T.Name,'')) AS [COUNT(Name)]
FROM CTE_DISTINCT_NAMES AS T
GROUP BY T.ProductId
Try this solution:
SELECT
PRODUCT_ID AS PID,
COALESCE(COUNT(NAME), 0) AS CNT
FROM YOUR_TABLE
GROUP BY PRODUCT_ID

Is it possible to create and use window function in the same query?

I'm using PostgreSQL and I have the following situation:
table of Sales (short version):
itemid quantity
5 10
5 12
6 1
table of stock (short version):
itemid stock
5 30
6 1
I have a complex query that also needs to present in one of it's columns the SUM of each itemid.
So it's going to be:
Select other things,itemid,stock, SUM (quantity) OVER (PARTITION BY itemid) AS total_sales
from .....
sales
stock
This query is OK. however this query will present:
itemid stock total_sales
5 30 22
6 1 1
But I don't need to see itemid=6 because the whole stock was sold. meaning that I need a WHERE condition like:
WHERE total_sales<stock
but I can't do that as the total_sales is created after the WHERE is done.
Is there a way to solve this without surrounding the whole query with another one? I'm trying to avoid it if I can.
You can use a subquery or CTE:
select s.*
from (Select other things,itemid,stock,
SUM(quantity) OVER (PARTITION BY itemid) AS total_sales
from .....
) s
where total_sales < stock;
You cannot use table aliases defined in a SELECT in the SELECT, WHERE, or FROM clauses for that SELECT. However, a subquery or CTE gets around this restriction.
You can also use an inner select in your WHERE statement like this:
SELECT *, SUM (quantity) OVER (PARTITION BY itemid) AS total_sales
FROM t
WHERE quantity <> (SELECT SUM(quantity) FROM t ti WHERE t.itemid = ti.itemid);

Join to replace sub-query

I am almost a novie in database queries.
However,I do understand why and how correlated subqueries are expensive and best avoided.
Given the following simple example - could someone help replacing with a join to help understand how it scores better:
SQL> select
2 book_key,
3 store_key,
4 quantity
5 from
6 sales s
7 where
8 quantity < (select max(quantity)
9 from sales
10 where book_key = s.book_key);
Apart from join,what other option do we have to avoid the subquery.
In this case, it ought to be better to use a windowed-function on a single access to the table - like so:
with s as
(select book_key,
store_key,
quantity,
max(quantity) over (partition by book_key) mq
from sales)
select book_key, store_key, quantity
from s
where quantity < s.mq
Using Common Table Expressions (CTE) will allow you to execute a single primary SELECT statement and store the result in a temporary result set. The data can then be self-referenced and accessed multiple times without requiring the initial SELECT statement to be executed again and won't require possibly expensive JOINs. This solution also uses ROW_NUMBER() and the OVER clause to number the matching BOOK_KEYs in descending order based off of the quantity. You will then only include the records that have a quantity that is less than the max quantity for each BOOK_KEY.
with CTE as
(
select
book_key,
store_key,
quantity,
row_number() over(partition by book_key order by quantity desc) rn
from sales
)
select
book_key,
store_key,
quantity
from CTE where rn > 1;
Working Demo: http://sqlfiddle.com/#!3/f0051/1
Apart from join,what other option do we have to avoid the subquery.
You use something like this:
SELECT select max(quantity)
INTO #myvar
from sales
where book_key = s.book_key
select book_key,store_key,quantity
from sales s
where quantity < #myvar