Getting confused by MySQL subqueries - mysql

I'm trying to learn how to do subqueries, and I'm really confused at what's wrong with this simple example.
My first try was
SELECT COUNT(SELECT * FROM my_table);
but that didn't work (I guess because I need a temporary table?) so I tried this:
SELECT COUNT(items)
FROM (SELECT * FROM my_table) AS items;
Why do I get the following:
1054: Unknown column 'items' in 'field list'

You're getting the error because in this example items is a table (as it is an alias), not a column. Simplest solution is to use:
SELECT COUNT(*)
FROM (SELECT * FROM my_table) AS items
Aggregate functions (IE: COUNT, MIN, MAX, AVG, etc) only work on column references, but some accept [table].* as a parameter.

Related

How to use subselect in MySQL to get all unique names and their count?

I'm trying to make an SQL query, that returns all the unique names and a sum of occurences for each name.
This is what I came up with, but it merely gets the sum of all names and not the sum of each name separately.
select distinct(etunimi) as etunimi,
(select count(distinct(etunimi)) as määrä from jasenet)
from jasenet;
Is this the right way to go when solving this problem or is there another way of achieving this? thank you.
If you group by a column then aggregate functions like count() apply to each group and not the complete result set.
select etunimi, count(*)
from jasenet
group by etunimi
That because you haven't reference the colomn from outerquery with subquery
So, it should be referenced like that :
select distinct etunimi,
(select count(*)
from jasenet j1
where j1.etunimi = j.etunimi
) as määrä
from jasenet j;
However, i would also suggest to use GROUP BY clause which is more efficient than correlated subquery.

Table alias is unknown in nested subquery

The following query works just fine. I am using a value from the outer select to filter inside the inner select.
SELECT
bk.ID,
(SELECT COUNT(*) FROM guests WHERE BookingID = bk.ID) as count
FROM
bookings bk;
However, the following select will not work:
SELECT
bk.ID,
(SELECT SUM(count) FROM (SELECT COUNT(*) AS count FROM guests WHERE BookingID = bk.ID GROUP BY RoomID) sub) as sumcount
FROM
bookings bk;
The error message is: Error Code: 1054. Unknown column 'bk.ID' in 'where clause'
Why is my alias bk known in the subselect, but not in the subselect of the subselect?
For the record, I am using MySQL 5.6.
This is called "scoping". I know that Oracle (for instance) only looks out one level for resolving table aliases. SQL Server is also consistent: it looks out more than one level.
Based on this example, MySQL clearly limits the scope of the identifier bk to the immediate subquery. There is a small hint in the documentation (emphasis mine):
A correlated subquery is a subquery that contains a reference to a
table that also appears in the outer query.
However, I have not found any other specific reference to the scoping rules in the documentation. There are other answers (here and here) that specify that the scope of a table alias is limited to one level of subquery.
You already know how to fix the problem (your two queries should produce identical results). Re-arranging the query to have joins and aggregations can also resolve this problem.
Correlated Scalar Subqueries in the SELECT list can usually be rewritten to a LEFT JOIN on a Derived Table (and in many cases they might perform better then):
SELECT
bk.ID,
dt.sumcount
FROM
bookings bk
LEFT JOIN
(SELECT BookingID,SUM(COUNT) AS sumcount
FROM
(
SELECT BookingID, RoomId, COUNT(*) AS COUNT
FROM guests
GROUP BY BookingID, RoomID
) sub
) AS dt
ON bk.BookingID = dt.BookingID

Select with UNION ALL and ORDER BY

I've trying to query from a few tables with UNION ALL but I get this error on the ORDER clause:
#1054 - Unknown column 'Time' in 'order clause'
I have column Time in the table. This is the query:
SELECT * from
(SELECT table, 'as' from as
UNION ALL
SELECT table, 'as' from as1
UNION ALL
SELECT table, 'as' from as2
UNION ALL
SELECT table, 'as' from as3) asAllWrong
WHERE table not like 'as%' OR length(table) < 12
ORDER BY Time='2015-06-02 9:00:00;
So, how exactly I can query this to show me all wrong entry from those tables and this table?
And why did I get this error even if there is a column called Time?
Edit: My mistake they are different tables as, as1, as2... And I want to query all wrong entries in that time as I said.
EDIT: This is what is look like now and is working good so far. Note that this is for me.. will not go in any production and will be used sometimes so I don't really need performance .. etc..
SELECT * from
( SELECT as.*, 'as' from as
UNION ALL
SELECT as1.*, 'as1' from as1
UNION ALL
SELECT as2.*, 'as2' from as2
UNION ALL
SELECT as3.*, 'as3' from as3) asAllWrong
WHERE as not like 'as%' OR length(as) < 12
ORDER BY Time='2015-06-02 9:00:00' ASC;
While the query looks very confusing, the bottom line is that your result in-memory table created via UNION ALL doesn't contain the field Time and that's why you have an error.
As for why it's not there, normally you can't just select {table_name}. It has to be a list of columns. But I am not an expert in MySql - it may allow such trickery. As Jonathan noted below, you'd be better of with:
select table.Time, 'as' from table
union all...
This would definitely bring Time field into the picture.
You need to specify also the Time column in your select
SELECT * from
(SELECT table, Time, 'as' from as
UNION ALL
SELECT table, Time, 'as' from as
UNION ALL
SELECT table, Time, 'as' from as
UNION ALL
SELECT table, Time, 'as' from as) asAllWrong
WHERE table not like 'as%' OR length(table) < 12
ORDER BY Time='2015-06-02 9:00:00' ASC/DESC;
This should work.
EDIT: Thank's for pointing my errors. I just use his example to show it.
Also as other said already you can user select as.*, 'as' from as... this will select all fields from as if you need them of course.
Also you may need ASC or DESC in your ORDER BY clause. ORDER BY sorts the records in ascending order by default.

GROUP BY clause with non aggregate functions

Why mysql allows use non aggregate functions with GROUP BY clause ?
For example, this query works fine:
SELECT col, CHAR_LENGTH(col) FROM table
GROUP BY col
There is acceptable using querys like this ?
Sometimes is quite acceptable. Your query, written in more standard SQL, would be something like:
SELECT col, CHAR_LENGTH(col)
FROM (SELECT col FROM table GROUP BY col) c
or as:
SELECT col, MAX(CHAR_LENGTH(col))
FROM table
GROUP BY col
using non aggregate functions you can simplify the query a little bit, but the query would be a little more difficult to read.
It could also be useful when you are sure that all non aggregated columns share the same value:
SELECT id, name, surname
FROM table
GROUP BY id
HAVING COUNT(*)=1
or when it doesn't matter which value you need to return:
SELECT id, name
FROM table
GROUP BY id
will return a single name associated to that id (probably the first name encountered, but we can't be sure which one is the first, order by doesn't help here...). Be warned that if you want to select multiple non aggregated columns:
SELECT id, name, surname
FROM table
GROUP BY id
we have no guarantees that the name and surname returned will belong to the same row.
I would prefer not to use this extension, unless you are 100% sure of why you are using it.
MySQL has some "improvements" and tries to run and return result from invalid queries, in example like yours every good RDBMS should throw syntax error, but MySQL will run it, group the result by col and put value of randomly chosen row into second column.
If I'm guessing correctly about what you want to do, DISTINCT is a better choice:
SELECT DISTINCT col, CHAR_LENGTH(col) FROM table;
It more clearly indicates the readers what you're trying to accomplish.
Here is a SQLFiddle.

MySQL user-defined function for getting the modal value of a column?

For example:
SELECT MODE(field) FROM table
In another mode, what user-defined function can I use to get the most common value of a column?
I know I can do something like:
SELECT field, COUNT(*) as total FROM table GROUP BY field ORDER BY total DESC LIMIT 1
But I have to query other data in the same MySQL statement too, so I have to use a user-defined function.
Thank you.
Here's a link to MySQL's documentation on aggregate functions. It looks like they don't have anything for "mode", so I would say that your second query is probably your best shot.
MySQL doesn't support user-defined aggregate functions (PostgreSQL does, for what it's worth). You can't use a UDF to do what you want in MySQL.
You can do it for example by putting the mode-computation in a derived table subquery:
SELECT t.*
FROM (SELECT field AS mode, COUNT(*) as total FROM table
GROUP BY field ORDER BY total DESC LIMIT 1) AS m
JOIN table t ON m.mode = t.field;