UNION and LIMIT 1 clarification - mysql

I have a terrible doubt.
I have a query like this:
SELECT id,fatherID FROM comments WHERE fatherID IS NULL
UNION
SELECT id,fatherID FROM comments WHERE fatherID IS NOT NULL
LIMIT 1
(Note the limit 1 applies to the union, not to only the second query)
(Note2: IS NULL, and IS NOT NULL are just an example, they can be a random string too)
With this limit 1 I am sure that this kind of query will return the row from the first query all the time?
Or even if both query returns something it can happen that the LIMIT 1 get the row coming from the second query?
I am asking this because If i run that query (wtihtou limit 1) I get this results
http://img856.imageshack.us/img856/5212/immaginejv.jpg
When I would except something like this (First all the rows with fatherid = null, then the other):
http://img847.imageshack.us/img847/479/immaginehx.jpg

Order of returned results is (by default) undefined. If you want them ordered in a specific manner, use ORDER BY, it was made to do exactly this.

I have a question regarding your query. Why you would require this query? You are nothing but selecting all rows by making union of both opposite queries i.e
fatherID IS NULL OR NOT NULL
How can duplicate rows will present in result by this union query?
SELECT id, fatherID
FROM comments
WHERE fatherID IS NULL
UNION
SELECT id, fatherID
FROM comments
WHERE fatherID IS NOT NULL
LIMIT 1
You can get result (First all the rows with fatherid = null, then the other): directly by this query:
SELECT id, fatherID
FROM comments
ORDER By fatherID
Updated answer:
SELECT *
FROM
(SELECT
id, fatherID
FROM
comments
WHERE
fatherID = 'somerandomstring1'
UNION
SELECT
id, fatherID
FROM
comments
WHERE
fatherID = 'somerandomstring2') combined_comments
ORDER BY
combined_comments.fatherID
LIMIT 1

Related

Count users and groups totals in one query

I am super new to access and having difficulty with a query. My table (tblpacks) is the following:
id user_id group_id quota_id
1 1 1 1
2 1 2 1
3 2 1 1
4 3 1 1
Ideally, what I now is to get hte number of unique users and groups for quota_id=1
The result will be:
total_users = 3
total_groups = 2
If you only wanted to count one field, there would by a simple solution, but since you want to count 2 separate fields, you in fact need at least 2 separate queries.
My answer is to use a UNION query as the source for counting. This UNION query returns the distinct user_id values (with Null as group_id) and the distinct group_id values (with Null as user_id). I omitted the DISTINCT keyword, because UNION (without ALL) does a DISTINCT query automatically. As the datatypes where not recognized correctly when using a constant Null field in the first SELECT statement of the UNION query, I added a third SELECT statement as the first one, which selects both fields from the table but returns no records:
SELECT Count(user_id) AS total_users, Count(group_id) AS total_groups
FROM (
SELECT user_id, group_id FROM tblpacks WHERE Yes=No
UNION
SELECT user_id, Null FROM tblpacks WHERE quota_id=1
UNION
SELECT Null, group_id FROM tblpacks WHERE quota_id=1
) AS qsub;

Insert empty row after group

I have a table that contains transaction data. The rows with the same 'group_id' are a part of the same transaction. I am running the following SQL query to show all the transactions:
SELECT * FROM transactions
When I run this query I get as expected a list of all the transactions. But this large list makes it difficult to seperate the data with a different group_id from the other data.
For that reason I want to add an empty row at the end of the group_id, so I get:
1
1
(empty row)
2
2
2
instead of:
1
1
2
2
2
Can someone help me with this?
Here is my database:
http://sqlfiddle.com/#!9/b9bf79/1
I do not suggest you do this at all but if you just want to separate two groups you could do this:
SELECT * FROM transactions WHERE group_id = 1
UNION ALL
(SELECT '','','','','','')
UNION ALL
SELECT * FROM transactions WHERE group_id = 2
Obviously this can added to if there are more group ids in the future but it is not a general purpose solution you are really better off dealing with appearance issues like this in application code.
you can use (abuse) rollup.
SELECT *
FROM transactions
group by group_id, id
with rollup
having group_id is not null
this will insert a row with id set to null after each group_id.
mysql will also sort by group_id because of the group by.
The group by id` makes sure that all rows are shown (your schema does not show it, but I assume id is unique? Otherwise you need to add other fields)
However only id will be null in the extra rows. The other columns repeat the value above.
You can filter them like this:
SELECT
id,
case id is not null when true then date else null end as date,
case id is not null when true then group_id else null end as group_id
-- ....
FROM transactions
group by group_id, id
with rollup
having group_id is not null
Alternatively:
select * from
(SELECT *
FROM transactions
union all
select distinct null, null, group_id, null, null,null from transactions
) as t
order by 3,1
but null values are sorted first, so the "gap" is in front of each section

How to use ORDER BY inside UNION

I want to use ORDER BY on every UNION ALL queries, but I can't figure out the right syntax. This is what I want:
(
SELECT id, user_id, other_id, name
FROM tablename
WHERE user_id = 123 AND user_in IN (...)
ORDER BY name
)
UNION ALL
(
SELECT id, user_id, other_id, name
FROM tablename
WHERE user_id = 456 AND user_id NOT IN (...)
ORDER BY name
)
EDIT:
Just to be clear: I need two ordered lists like this, not one:
1
2
3
1
2
3
4
5
Thank you very much!
Something like this should work in MySQL:
SELECT a.*
FROM (
SELECT ... FROM ... ORDER BY ...
) a
UNION ALL
SELECT b.*
FROM (
SELECT ... FROM ... ORDER BY ...
) b
to return rows in an order we'd like them returned. i.e. MySQL seems to honor the ORDER BY clauses inside the inline views.
But, without an ORDER BY clause on the outermost query, the order that the rows are returned is not guaranteed.
If we need the rows returned in a particular sequence, we can include an ORDER BY on the outermost query. In a lot of use cases, we can just use an ORDER BY on the outermost query to satisfy the results.
But when we have a use case where we need all the rows from the first query returned before all the rows from the second query, one option is to include an extra discriminator column in each of the queries. For example, add ,'a' AS src in the first query, ,'b' AS src to the second query.
Then the outermost query could include ORDER BY src, name, to guarantee the sequence of the results.
FOLLOWUP
In your original query, the ORDER BY in your queries is discarded by the optimizer; since there is no ORDER BY applied to the outer query, MySQL is free to return the rows in whatever order it wants.
The "trick" in query in my answer (above) is dependent on behavior that may be specific to some versions of MySQL.
Test case:
populate tables
CREATE TABLE foo2 (id INT PRIMARY KEY, role VARCHAR(20)) ENGINE=InnoDB;
CREATE TABLE foo3 (id INT PRIMARY KEY, role VARCHAR(20)) ENGINE=InnoDB;
INSERT INTO foo2 (id, role) VALUES
(1,'sam'),(2,'frodo'),(3,'aragorn'),(4,'pippin'),(5,'gandalf');
INSERT INTO foo3 (id, role) VALUES
(1,'gimli'),(2,'boromir'),(3,'elron'),(4,'merry'),(5,'legolas');
query
SELECT a.*
FROM ( SELECT s.id, s.role
FROM foo2 s
ORDER BY s.role
) a
UNION ALL
SELECT b.*
FROM ( SELECT t.id, t.role
FROM foo3 t
ORDER BY t.role
) b
resultset returned
id role
------ ---------
3 aragorn
2 frodo
5 gandalf
4 pippin
1 sam
2 boromir
3 elron
1 gimli
5 legolas
4 merry
The rows from foo2 are returned "in order", followed by the rows from foo3, again, "in order".
Note (again) that this behavior is NOT guaranteed. (The behavior we observer is a side effect of how MySQL processes inline views (derived tables). This behavior may be different in versions after 5.5.)
If you need the rows returned in a particular order, then specify an ORDER BY clause for the outermost query. And that ordering will apply to the entire resultset.
As I mentioned earlier, if I needed the rows from the first query first, followed by the second query, I would include a "discriminator" column in each query, and then include the "discriminator" column in the ORDER BY clause. I would also do away with the inline views, and do something like this:
SELECT s.id, s.role, 's' AS src
FROM foo2 s
UNION ALL
SELECT t.id, t.role, 't' AS src
FROM foo3 t
ORDER BY src, role
Don't use ORDER BY in an individual SELECT statement inside a UNION, unless you're using LIMIT with it.
The MySQL docs on UNION explain why (emphasis mine):
To apply ORDER BY or LIMIT to an individual SELECT, place the clause
inside the parentheses that enclose the SELECT:
(SELECT a FROM t1 WHERE a=10 AND B=1 ORDER BY a LIMIT 10) UNION
(SELECT a FROM t2 WHERE a=11 AND B=2 ORDER BY a LIMIT 10);
However, use of ORDER BY for individual SELECT statements implies
nothing about the order in which the rows appear in the final result
because UNION by default produces an unordered set of rows. Therefore,
the use of ORDER BY in this context is typically in conjunction with
LIMIT, so that it is used to determine the subset of the selected rows
to retrieve for the SELECT, even though it does not necessarily affect
the order of those rows in the final UNION result. If ORDER BY appears
without LIMIT in a SELECT, it is optimized away because it will have
no effect anyway.
To use an ORDER BY or LIMIT clause to sort or limit the entire UNION
result, parenthesize the individual SELECT statements and place the
ORDER BY or LIMIT after the last one. The following example uses both
clauses:
(SELECT a FROM t1 WHERE a=10 AND B=1)
UNION
(SELECT a FROM t2 WHERE a=11 AND B=2)
ORDER BY a LIMIT 10;
It seems like an ORDER BY clause like the following will get you what you want:
ORDER BY user_id, name
You just use one ORDER BY at the very end.
The Union turns two selects into one logical select. The order-by applies to the entire set, not to each part.
Don't use any parens either. Just:
SELECT 1 as Origin, blah blah FROM foo WHERE x
UNION ALL
SELECT 2 as Origin, blah blah FROM foo WHERE y
ORDER BY Origin, z
(SELECT id, user_id, other_id, name
FROM tablename
WHERE user_id = 123
AND user_in IN (...))
UNION ALL
(SELECT id, user_id, other_id, name
FROM tablename
WHERE user_id = 456
AND user_id NOT IN (...)))
ORDER BY name
You can also simplify this query:
SELECT id, user_id, other_id, name
FROM tablename
WHERE (user_id = 123 AND user_in IN (...))
OR (user_id = 456 AND user_id NOT IN (...))

Search in two tables and order by occurrence of term

I have this query to search in two SQL tables. I am looking for a way to sort the result by occurrence. This is my query:
SELECT `parent_id`
FROM wp_forum_posts
WHERE text LIKE '%{$term1}%'
UNION
SELECT `id`
FROM wp_forum_threads
WHERE subject LIKE '%{$term1}%
Which is the best way, to get the results ordered?
The trick is first to use UNION ALL, which preserves duplicates (ordinary UNION removes duplicates), then select from that result. This query should do it:
select * from (
select parent_id as mID, count(*) as cnt
from wp_forum_posts
where text like '%{$term1}%'
group by 1
UNION ALL
select id, count(*)
FROM wp_forum_threads
where subject like '%{$term1}%
group by 1) x
order by 2, 1
Assumes ID and parent_ID are not duplicates in tables otherwise you can get 2 rows per an id... and would you want them summed together if so then are parent_ID and ID related?
Select mID, cnt
FROM
(SELECT `parent_id` as mID, count(`parent_ID`) as cnt
FROM wp_forum_posts
WHERE text LIKE '%{$term1}%'
Group by `parent_ID`
UNION
SELECT `id`, count(`id`) as cnt
FROM wp_forum_threads
WHERE subject LIKE '%{$term1}%
GROUP BY `id`)
Order by cnt ASC, mID

Get most recent row for given ID

In the table below, how do I get just the most recent row with id=1 based on the signin column, and not all 3 rows?
+----+---------------------+---------+
| id | signin | signout |
+----+---------------------+---------+
| 1 | 2011-12-12 09:27:24 | NULL |
| 1 | 2011-12-13 09:27:31 | NULL |
| 1 | 2011-12-14 09:27:34 | NULL |
| 2 | 2011-12-14 09:28:21 | NULL |
+----+---------------------+---------+
Use the aggregate MAX(signin) grouped by id. This will list the most recent signin for each id.
SELECT
id,
MAX(signin) AS most_recent_signin
FROM tbl
GROUP BY id
To get the whole single record, perform an INNER JOIN against a subquery which returns only the MAX(signin) per id.
SELECT
tbl.id,
signin,
signout
FROM tbl
INNER JOIN (
SELECT id, MAX(signin) AS maxsign FROM tbl GROUP BY id
) ms ON tbl.id = ms.id AND signin = maxsign
WHERE tbl.id=1
SELECT *
FROM tbl
WHERE id = 1
ORDER BY signin DESC
LIMIT 1;
The obvious index would be on (id), or a multicolumn index on (id, signin DESC).
Conveniently for the case, MySQL sorts NULL values last in descending order. That's what you typically want if there can be NULL values: the row with the latest not-null signin.
To get NULL values first:
ORDER BY signin IS NOT NULL, signin DESC
You may want to append more expressions to ORDER BY to get a deterministic pick from (potentially) multiple rows with NULL.
The same applies without NULL if signin is not defined UNIQUE.
Related:
mysql order by, null first, and DESC after
The SQL standard does not explicitly define a default sort order for NULL values. The behavior varies quite a bit across different RDBMS. See:
https://docs.mendix.com/refguide/null-ordering-behavior
But there are the NULLS FIRST / NULLS LAST clauses defined in the SQL standard and supported by most major RDBMS, but not by MySQL. See:
SQL how to make null values come last when sorting ascending
Sort by column ASC, but NULL values first?
Building on #xQbert's answer's, you can avoid the subquery AND make it generic enough to filter by any ID
SELECT id, signin, signout
FROM dTable
INNER JOIN(
SELECT id, MAX(signin) AS signin
FROM dTable
GROUP BY id
) AS t1 USING(id, signin)
Select [insert your fields here]
from tablename
where signin = (select max(signin) from tablename where ID = 1)
SELECT * FROM (SELECT * FROM tb1 ORDER BY signin DESC) GROUP BY id;
I had a similar problem. I needed to get the last version of page content translation, in other words - to get that specific record which has highest number in version column. So I select all records ordered by version and then take the first row from result (by using LIMIT clause).
SELECT *
FROM `page_contents_translations`
ORDER BY version DESC
LIMIT 1
Simple Way To Achieve
I know it's an old question
You can also do something like
SELECT * FROM Table WHERE id=1 ORDER BY signin DESC
In above, query the first record will be the most recent record.
For only one record you can use something like
SELECT top(1) * FROM Table WHERE id=1 ORDER BY signin DESC
Above query will only return one latest record.
Cheers!