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.
Related
i have query
SELECT t1.account AS main, t1.id, tmp.* FROM account.account t1 LEFT JOIN
(
SELECT
tmp1.account,
tmp1.lot - tmp2.lot AS nett
FROM
( SELECT account, SUM( lot ) AS lot
FROM orders WHERE market_date = "2020-07-20"
AND `transaction` = 1 AND account = t1.id ) AS
tmp1
LEFT JOIN ( SELECT account, SUM( lot ) AS lot
FROM orders WHERE market_date = "2020-07-20"
AND `transaction` = 2 AND account = t1.id ) AS
tmp2 ON tmp1.account = tmp2.account
) tmp ON tmp.account = t1.id;
and the result is
1054 - Unknown column 't1.id' in 'where clause', Time: 0.001000s
how to access t1.id current subquery select ?
You may try just using a join to a single level subquery on orders:
SELECT
a.account AS main,
a.id,
COALESCE(o.nett, 0) AS nett
FROM account a
LEFT JOIN
(
SELECT
account,
SUM(CASE WHEN `transaction` = 1 THEN lot END) -
SUM(CASE WHEN `transaction` = 2 THEN lot END) AS nett
FROM orders
WHERE market_date = '2020-07-20'
GROUP BY account
) o
ON o.account = a.account;
I have the following query:
SELECT c.text1, sum(c.num1), sum(c.num2), sum(c.num3),
(SELECT count(id) FROM table2 WHERE type = 1 AND txt = c.text1 AND spec_id = c.sp_id)
FROM table1 as c
WHERE c.type = 1
GROUP BY c.text1, c.sp_id
Is there a workaround to loose the c.sp_id from the GroupBy clause somehow? I know that if I remove it MySQL will return an error.
Or is there a way to group the results of this query by c.text1 only?
If I understand the problem correctly, you need to do two separate aggregations. This is one version of the query:
SELECT c.text1, c.sum1, c.sum2, c.sum3, t2.cnt
FROM (SELECT c.text1, sum(c.num1) as sum1, sum(c.num2) as cum2, sum(c.num3) as sum3
FROM table1 c
GROUP BY c.text1
) c LEFT JOIN
(SELECT txt, count(*) as cnt
FROM table2 t2
WHERE t2.type = 1 AND
EXISTS (SELECT 1
FROM table1 c2
WHERE t.txt = c2.txt AND c2.type = 1 AND
t.spec_id = c2.sp_id
)
) t2
ON t2.txt = c.text1
WHERE c.type = 1;
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'm selecting transactions from a table using the following query:
SELECT t.*
FROM transactions AS t
WHERE t.id IN
(
SELECT t2.id
FROM `virtual_account-account` AS vaa
LEFT JOIN transactions AS t2
ON t2.account = vaa.account
WHERE vaa.virtual_account = 3
)
AND t.id IN
(
SELECT tt.transaction
FROM `virtual_account-tag` AS vat
LEFT JOIN `transaction-tag` AS tt
ON tt.tag = vat.tag
WHERE vat.virtual_account = 3
)
ORDER BY t.date
However, I'd like to modify this such that if, e.g., the junction table virtual_account-account does not contain any junctions where vaa.virtual_account = 3, then the corresponding t.id IN (...) condition should be ignored. Thus far, I've failed to find a solution for this so any help would be highly appreciated.
Just an OR between both conditions:
SELECT t.*
FROM transactions AS t
WHERE t.id IN
(
SELECT t2.id
FROM `virtual_account-account` AS vaa
LEFT JOIN transactions AS t2
ON t2.account = vaa.account
WHERE vaa.virtual_account = 3
)
OR t.id IN
(
SELECT tt.transaction
FROM `virtual_account-tag` AS vat
LEFT JOIN `transaction-tag` AS tt
ON tt.tag = vat.tag
WHERE vat.virtual_account = 3
)
ORDER BY t.date
If one is returning nothing, it will still return the other transactions.
The following approach turns the subquery into a join and uses a variable to count the number of rows in the subquery. The where clause then uses this variable for the logic you want:
SELECT t.*
FROM transactions t left outer join
(SELECT t2.id, #cnt := #cnt + 1
FROM `virtual_account-account` AS vaa
LEFT JOIN transactions AS t2
ON t2.account = vaa.account cross join
(select #cnt := 0)
WHERE vaa.virtual_account = 3
group by t2.id
) c1
on c1.id = t.id
WHERE (c1.id is not null or #cnt = 0) and
t.id IN (SELECT tt.transaction
FROM `virtual_account-tag` AS vat
LEFT JOIN `transaction-tag` AS tt
ON tt.tag = vat.tag
WHERE vat.virtual_account = 3
)
ORDER BY t.date;
Note that the subquery eliminates duplicates (uses group by). Otherwise, you might have the problem of multiple matches resulting in multiple rows.
A bit clumsy and verbose, but...
SELECT t.*
FROM transactions AS t
WHERE t.id IN
(
SELECT t2.id
FROM `virtual_account-account` AS vaa
LEFT JOIN transactions AS t2
ON t2.account = vaa.account
WHERE vaa.virtual_account = 3
)
AND t.id IN
(
SELECT tt.transaction
FROM `virtual_account-tag` AS vat
LEFT JOIN `transaction-tag` AS tt
ON tt.tag = vat.tag
WHERE vat.virtual_account = 3
AND EXISTS
(
SELECT t2.id
FROM `virtual_account-account` AS vaa
LEFT JOIN transactions AS t2
ON t2.account = vaa.account
WHERE vaa.virtual_account = 3
)
)
ORDER BY t.date
Try:
SELECT t.*
FROM (select count(*) vaa
FROM `virtual_account-account`
WHERE virtual_account = 3) a
CROSS JOIN (select count(*) vat
FROM `virtual_account-tag`
WHERE virtual_account = 3) g
CROSS JOIN transactions AS t
LEFT JOIN `virtual_account-account` vaa
ON t.id = vaa.id AND vaa.virtual_account = 3
LEFT JOIN `virtual_account-tag` vat
JOIN `transaction-tag` tt ON tt.tag = vat.tag
ON t.id = tt.transaction AND vat.virtual_account = 3
WHERE (a.vaa = 0 OR vaa.id IS NOT NULL) AND
(g.vat = 0 OR vat.tag IS NOT NULL)
GROUP BY t.id
ORDER BY t.date
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)