I have this SQL query:
SELECT id, COUNT(*) AS price
FROM (SELECT * FROM rt WHERE somecondition) AS st
JOIN tt
ON st.id = tt.id
GROUP BY id;
Now, I want to select all rows which have the maximum price of the table. I have tried this, which unfortunately returns no row at all:
SELECT id, COUNT(*) AS price
FROM (SELECT * FROM rt WHERE somecondition) AS st
JOIN tt
ON st.id = tt.id
GROUP BY id
HAVING price = MAX(price);
I'm somewhat lost, does anybody have any pointers?
This looks fairly simple to me:
select * from <table>
where <column name> in(
SELECT MAX(column name) FROM table
)
Try this solution:
SELECT a.id, a.price
FROM
(
SELECT aa.id, COUNT(1) AS price
FROM rt aa
INNER JOIN tt bb ON aa.id = bb.id
WHERE aa.somecondition
GROUP BY aa.id
) a
INNER JOIN
(
SELECT MAX(aa.price) AS maxprice
FROM
(
SELECT COUNT(1) AS price
FROM rt aaa
INNER JOIN tt bbb ON aaa.id = bbb.id
WHERE aaa.somecondition
GROUP BY aaa.id
) aa
) b ON a.price = b.maxprice
Edit: While I can't think of any way to rewrite this so as to not have to write the base-queries redundantly, what you could perhaps do is this:
SELECT GROUP_CONCAT(a.id) AS ids, a.price
FROM
(
SELECT aa.id, COUNT(1) AS price
FROM rt aa
INNER JOIN tt bb ON aa.id = bb.id
WHERE aa.somecondition
GROUP BY aa.id
) a
GROUP BY a.price
ORDER BY a.price DESC
LIMIT 1
This produces a comma-separated-list of the ids that share the same maximum value. This is probably not the format you are looking for though, but it is one way to avoid having to write the base-query twice. Just putting that out there.
try this, put MAX in select, this should be the correct way
SELECT id, COUNT(*) AS price, MAX(price) AS max_price
FROM (SELECT some_table_name FROM rt WHERE somecondition LIMIT 1) AS st
JOIN thenextTable as tt
ON st.id = tt.id
GROUP BY id;
Assuming that #Zane's answer is what you do want, here's a portable version of his query that also avoids LIMIT/TOP operations. I'm not really familiar with mysql dialects, but I imagine this will work without problem.
SELECT a.id, a.price
FROM (
SELECT aa.id, COUNT(1) AS price
FROM rt aa
INNER JOIN tt bb ON aa.id = bb.id
WHERE [somecondition]
GROUP BY aa.id
) a
WHERE
a.price >= ALL (
SELECT COUNT(1) AS maxprice
FROM rt aa
INNER JOIN tt bb ON aa.id = bb.id
WHERE [somecondition]
GROUP BY aa.id
)
HAVING is used to check conditions after the aggregation takes place.
WHERE is used before the aggregation takes place.
SELECT id, COUNT(*) AS price
FROM (SELECT * FROM rt WHERE somecondition) AS st
JOIN tt
ON st.id = tt.id
WHERE price = (SELECT MAX(price) FROM ...table)
GROUP BY id
You asked for an approach that didn't require the redundancy of stating the inner query more than once. That's certainly what a cte is good for. These are two other solutions rewritten to use that tactic.
WITH basequery as (
SELECT aa.id, COUNT(1) AS price
FROM rt aa INNER JOIN tt bb ON aa.id = bb.id
WHERE [aa.somecondition]
GROUP BY aa.id
)
SELECT a.id, a.price
FROM
basequery as a INNER JOIN
(SELECT MAX(price) AS maxprice FROM basequery) as b
ON a.price = b.maxprice
-- or
WITH basequery as (
SELECT aa.id, COUNT(1) AS price
FROM rt aa INNER JOIN tt bb ON aa.id = bb.id
WHERE [aa.somecondition]
GROUP BY aa.id
)
SELECT a.id, a.price
FROM
basequery as a
WHERE
a.price >= ALL (SELECT price FROM basequery)
Related
i have this query which works but only gives the first five rows from the tables. see my former question -> multiple SELECT statements using CTE
WITH
cte AS ( SELECT n.name,
e.value,
ROW_NUMBER() OVER (PARTITION BY e.value
ORDER BY e.id) AS rn
from entries e
LEFT JOIN nodes n on n.id=e.node_id
LEFT JOIN attribs a on a.id=e.attrib_id
WHERE a.name = 'LOCATION'
AND e.value IN ('Wienerberg', 'Gruberstrasse')
AND DATE(ts) = CURRENT_DATE
ORDER BY e.id
),
nums AS ( SELECT 1 rn UNION
SELECT 2 UNION
SELECT 3 UNION
SELECT 4 UNION
SELECT 5
)
SELECT t1.name LNZ, t2.name WBG
FROM nums
LEFT JOIN cte t1 ON nums.rn = t1.rn
LEFT JOIN cte t2 ON nums.rn = t2.rn
WHERE t1.value = 'Gruberstrasse'
AND t2.value = 'Wienerberg'
-- AND COALESCE(t1.name, t2.name)
ORDER BY nums.rn
+----------------------+----------------------+
| LNZ | WBG |
+----------------------+----------------------+
| AIXVAEBDBT | KUG01148_JBOSS-T6 |
| OOEGKKT6 | AIXMVBMIGTA2 |
| HSR5S1P8_AM | KUG01115_WSAP_HA_LPM |
| AIXSTP11R3APP | AIXTESTHA2C1_HA_LPM |
| HSR3S1P10_OOEGKKTEST | KUG01142_STP17PR_HA |
+----------------------+----------------------+
how to i get all the rows without limiting to 5 rows? i skimmed through the mysql cte docs, but this seems to complex for me.
You are selecting from nums which will always return 5 rows.
Select from cte and make left join with nums.
You need 2 CTEs, for the 2 cases of 'Gruberstrasse' and 'Wienerberg' and then apply to them a simulated FULL JOIN (because MySql/MariaDB do not support a FULL JOIN):
WITH
cte1 AS ( SELECT n.name, e.value,
ROW_NUMBER() OVER (PARTITION BY e.value ORDER BY e.id) AS rn
FROM entries e
LEFT JOIN nodes n ON n.id = e.node_id
LEFT JOIN attribs a ON a.id = e.attrib_id
WHERE a.name = 'LOCATION' AND e.value = 'Gruberstrasse' AND DATE(ts) = CURRENT_DATE
),
cte2 AS ( SELECT n.name, e.value,
ROW_NUMBER() OVER (PARTITION BY e.value ORDER BY e.id) AS rn
FROM entries e
LEFT JOIN nodes n ON n.id = e.node_id
LEFT JOIN attribs a ON a.id = e.attrib_id
WHERE a.name = 'LOCATION' AND e.value = 'Wienerberg' AND DATE(ts) = CURRENT_DATE
)
SELECT c1.name LNZ, c2.name WBG
FROM cte1 c1 LEFT JOIN cte2 c2
ON c2.rn = c1.rn
UNION
SELECT c1.name LNZ, c2.name WBG
FROM cte2 c2 LEFT JOIN cte1 c1
ON c2.rn = c1.rn
Note that applying the condition:
a.name = 'LOCATION'
in the WHERE clause makes your LEFT join actually an INNER JOIN.
If the code works then fine, but if not then you should move this condition in the ON clause.
The same applies for the condition:
DATE(ts) = CURRENT_DATE
if ts is not a column of the table entries.
Before anything, since you're using MariaDB version above 10.1, you're in luck. MariaDB has such a thing called Storage Sequence Engine built-in. This thing can give you as many numbering sequence you want. So instead of doing:
SELECT 1 rn UNION
SELECT 2 UNION
SELECT 3 UNION
SELECT 4 UNION
SELECT 5
You can just do:
SELECT seq AS rn FROM seq_1_to_1000;
This will directly give you numbering sequence from 1 to 1000. Of course, you can add more than that and even using some other function than just running numbers. You can refer to the documentation link I've provided above.
The next thing I assume would be, "what if the rows are not even more than 100?". Like that, you don't need to change the numbering sequence but you can add another HAVING function at the end of the query, and maybe some IFNULL in the SELECT to replace NULL with some value.
Maybe something like this:
WITH
cte AS ( SELECT n.name,
e.value,
ROW_NUMBER() OVER (PARTITION BY e.value
ORDER BY e.id) AS rn
from entries e
LEFT JOIN nodes n on n.id=e.node_id
LEFT JOIN attribs a on a.id=e.attrib_id
WHERE a.name = 'LOCATION'
AND e.value IN ('Wienerberg', 'Gruberstrasse')
AND DATE(ts) = CURRENT_DATE
ORDER BY e.id
),
nums AS ( SELECT seq AS rn FROM seq_1_to_1000 )
SELECT IFNULL(t1.name,0) LNZ,
IFNULL(t2.name,0) WBG
FROM nums
LEFT JOIN cte t1 ON nums.rn = t1.rn
LEFT JOIN cte t2 ON nums.rn = t2.rn
WHERE t1.value = 'Gruberstrasse'
AND t2.value = 'Wienerberg'
-- AND COALESCE(t1.name, t2.name)
HAVING (0) NOT IN ('LNZ','WBG')
ORDER BY nums.rn;
Try this if it would work.
EDIT:
Here's another suggestion
SELECT Seq,
IFNULL(GROUP_CONCAT(CASE WHEN B.value='Gruberstrasse' THEN B.Name END),0) AS 'LNZ',
IFNULL(GROUP_CONCAT(CASE WHEN B.value='Wienerberg' THEN B.Name END),0) AS 'WBG'
FROM seq_1_to_1000 A LEFT JOIN
(SELECT n.name, e.value,
ROW_NUMBER() OVER (PARTITION BY e.value
ORDER BY e.id) AS rn
FROM entries e
LEFT JOIN nodes n ON n.id=e.node_id
LEFT JOIN attribs a ON a.id=e.attrib_id
WHERE a.name = 'LOCATION'
AND e.value IN ('Wienerberg', 'Gruberstrasse')
AND DATE(ts) = CURRENT_DATE
ORDER BY e.id) B
ON A.seq=B.rn GROUP BY A.seq
HAVING LNZ+WBG <> 0;
Without using cte:
LEFT JOIN the numbering sequence directly with the sub-query you originally made for the cte.
Using GROUP_CONCAT with CASE expression in SELECT then GROUP BY numbering sequence - added IFNULL to return zero and use that as filter in HAVING.
Example:
SELECT SUM(SALARY) FROM (SELECT * FROM table1 WHERE id > 10) a LEFT JOIN table2 b on a.person = b.person
I want join table2 records only to (SELECT * FROM table1 WHERE id > 10) records, my example is not correct.
table1 contain 100mln records and I cant join table2 to all records I must use subquery
I'm assuming, you salary is not summing up correctly (you are getting more than you expect). This is because LEFT JOIN will leave NULL for the rowsthat doesn't have match in b.
For this SQL:
SELECT a.*, b.*
FROM (select * from (SELECT 123 AS Salary,
'Tom' AS person
UNION
SELECT 343 AS Salary,
'Bob' AS person
UNION
SELECT 877 AS Salary,
'Tom' AS person) as t where t.Salary > 123) a
LEFT JOIN (SELECT *
FROM (SELECT 'Tom' AS person,
1 AS id
UNION
SELECT 'Bob' AS person,
2 AS id) AS t
WHERE t.id = 1) AS b
ON a.person = b.person
you will have this output:
So INNER JOIN should work for you.
SELECT SUM(SALARY) FROM (SELECT * FROM table1 WHERE id > 10) a
LEFT JOIN table2 b on a.person = b.person
Hopefully this will get you going in the correct direction....
select sum(a.salary)
from table1 a
left join table2 b on a.person = b.person and b.salary_type = "something"
where a.id > 10
;
I wrote this:
SELECT DISTINCT CATEGORY FROM T AS T1
CROSS JOIN (SELECT *
FROM T
WHERE T.CATEGORY = T1.CATEGORY
ORDER BY CATEGORY DESC
LIMIT 10)
and I receive this
"Unknown column 'T1.CATEGORY' in 'where clause'".
Why?
Update:
My purpose of this is to get 10 posts of any category.
Because T1 is not visible from within the subquery.
Your JOIN also serves no purpose and/or you probably forgot the JOIN condition.
In JOIN condition should use ON keyword
SELECT DISTINCT CATEGORY FROM T AS T1
CROSS JOIN SELECT * FROM T ON T.CATEGORY = T1.CATEGORY
ORDER BY CATEGORY DESC LIMIT 10;
If you need to get 10 posts of each category you can use a query like this:
SELECT CATEGORY, Post
FROM (
SELECT a.CATEGORY, a.Post, count(*) as rn
FROM #T a
JOIN #T b ON a.CATEGORY = b.CATEGORY AND a.Post >= b.Post
GROUP BY a.CATEGORY, a.Post) dt
WHERE rn < 11;
I have the following query:
SELECT
a.name, a.address, n.date, n.note
FROM a
LEFT JOIN n ON a.id = n.id
The a.id has a one to many relationship with n.id, so that many notes can be assocaited with one name.
How do I return just the latest note for each name instead of all the notes?
I'm using SQL Server 2008.
Thanks.
Here's one way using ROW_NUMBER()
SELECT t.name, t.address, t.date, t.note
FROM (
SELECT
a.name, a.address, n.date, n.note,
ROW_NUMBER() OVER (PARTITION BY a.name ORDER BY n.date DESC) rn
FROM a
LEFT JOIN n ON a.id = n.id
) t
WHERE t.rn = 1
alternative you can use a correlated subquery too get the max date, something like this
SELECT
a.name, a.address, n.date, n.note
FROM a
LEFT JOIN n ON a.id = n.id
WHERE n.date = (SELECT MAX(nn.date)
FROM n AS nn
WHERE a.id = nn.id)
i have a litte problem, i have a MySQL query code like this:
SELECT a.id, b.name AS itemName, b.slug AS itemSlug FROM table_idx a
INNER JOIN table_product b ON b.id = a.targetid AND a.target='PRODUCT'
WHERE a.memberid=$memberid
UNION
SELECT a.id, c.name AS itemName, c.slug AS itemSlug FROM table_idx a
INNER JOIN table_news c ON c.id = a.targetid AND a.target='NEWS'
WHERE a.memberid=$memberid
UNION
SELECT a.id, d.name AS itemName, d.slug AS itemSlug FROM table_idx a
INNER JOIN table_promo d ON d.id = a.targetid AND a.target='PROMO'
WHERE a.memberid=$memberid
How to add ORDER BY a.id DESC LIMIT 0,10 in query above? Thanks for your help.
Wrap it with another select.
SELECT * FROM (
SELECT a.id, b.name AS itemName, b.slug AS itemSlug FROM table_idx a
INNER JOIN table_product b ON b.id = a.targetid AND a.target='PRODUCT'
WHERE a.memberid=$memberid
UNION
SELECT a.id, c.name AS itemName, c.slug AS itemSlug FROM table_idx a
INNER JOIN table_news c ON c.id = a.targetid AND a.target='NEWS'
WHERE a.memberid=$memberid
UNION
SELECT a.id, d.name AS itemName, d.slug AS itemSlug FROM table_idx a
INNER JOIN table_promo d ON d.id = a.targetid AND a.target='PROMO'
WHERE a.memberid=$memberid
) AS t ORDER BY t.id DESC LIMIT 0,10
Unless your intensions are to ORDER BY and LIMIT each query befor the UNION, Just add
ORDER BY id DESC LIMIT 0,10
At the bottom of the query.