I have this query:
SELECT category, description, price, date_added, datetime_created
FROM vc_expense
WHERE trip_id=? AND description LIKE ?
GROUP BY description, price
UNION SELECT category, description, NULL, NULL, NULL
FROM vc_expense_default
WHERE description LIKE ?
ORDER BY CASE
WHEN description LIKE ? AND price THEN 1
WHEN price THEN 2
ELSE 3
END, date_added DESC, datetime_created DESC
LIMIT 5
My problem is that when I GROUP the description+price on row #4, it doesn't take into account that I want the most recent results:
date_added DESC, datetime_created DESC
Is there a way to use ORDER BY after row #4 so I can get the newest items only, it doesn't seem to work because of the UNION
Thanks!
Edit:
The UNION was irrelevant, I just had put it inside parantheses, and get the latest items only from each group, like this:
SELECT e1.category, e1.description, e1.price, e1.date_added, e1.datetime_created
FROM vc_expense e1
LEFT JOIN vc_expense e2 ON (
e1.description=e2.description AND
e1.price=e2.price AND
e1.date_added < e2.date_added
)
WHERE e2.id IS NULL AND e1.trip_id = ? AND e1.description LIKE ?
From the docs:
https://dev.mysql.com/doc/refman/5.7/en/union.html
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
and here's what you need:
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);
Related
I have the following list of items
Category
OrderNum
Prerequisites
2
NULL
4
Prerequisites
6
Sign Off
8
Sign Off
10
I would like it to be ordered so that 'Prerequisites' is together and the NULL category appears after it, so that:
Category
OrderNum
Prerequisites
2
Prerequisites
6
NULL
4
Sign Off
8
Sign Off
10
Currently my SQL has the following order by:
ORDER BY OrderNum <> '' DESC, OrderNum
I've tried the following, however it puts NULL at the end.
ORDER BY COALESCE(Category,'') <> '' DESC, OrderNum <> '' DESC, OrderNum
I'm trying to achieve it so that the records with the same category are together in the recordset, the NULL item should appear before the 'Sign Off' category because the OrderNum of NULL is less than any of the 'Sign Off' records.
I'm not sure if that's possible in one query. Any help would be appreciated.
Thanks!
You can apply all the conditions that you want with a CASE expression:
SELECT * FROM tablename
ORDER BY CASE
WHEN Category = 'Prerequisites' THEN 1
WHEN Category IS NULL THEN 2
ELSE 3
END,
Category,
OrderNum;
or, if there are also empty strings in Category which you want sorted with the NULLs:
SELECT * FROM tablename
ORDER BY CASE
WHEN Category = 'Prerequisites' THEN 1
WHEN COALESCE(Category, '') = '' THEN 2
ELSE 3
END,
Category,
OrderNum;
or:
SELECT * FROM tablename
ORDER BY Category = 'Prerequisites' DESC,
Category IS NULL DESC, -- or: COALESCE(Category, '') = '' DESC,
Category,
OrderNum;
But, if what you want is to sort the rows by the minimum OrderNum of each Category use a correlated subquery:
SELECT t1.* FROM tablename t1
ORDER BY (SELECT MIN(t2.OrderNum) FROM tablename t2 WHERE t2.Category = t1.Category),
t1.OrderNum;
or, for MySql 8.0+ use MIN() window function:
SELECT * FROM tablename
ORDER BY MIN(OrderNum) OVER (PARTITION BY Category),
OrderNum;
See the demo.
If you want the NULL values right after Prerequisites, you can use PrerequisitesZ as the fallback for NULL values:
ORDER BY COALESCE(Category, 'PrerequisitesZ') DESC, OrderNum;
You can try using two conditions in the ORDER BY clause:
if Category has the value "Prerequisites", it should come first
otherwise order by the OrderNum value
Here's how you would do it:
SELECT *
FROM tab
ORDER BY IF(Category='Prerequisites', 0, 1),
OrderNum
Demo here.
Note: Actually you can play how much you want with the conditions in the ORDER BY clause, as it can accept most of MySQL constructs.
I have a table
t1
--------------
id
date
val_1
val_2
I need to get 10 latest results and have them sorted from earlier to latest date, so I do it as
SELECT * FROM (
SELECT *
FROM myTable
WHERE project = $project_id
ORDER BY date DESC
LIMIT 10) AS results ORDER BY id ASC
Now, I need to get the highest value of val_1. I am trying to build a bar graph and would like to get the highest value because I need to divide it by the max height and use that result as a multiplier to stack-up val_1 and val_2 on top of each other without going outside of the graph's height.
So you want something like this?
SELECT *,
t2.biggest_value,
((t1.val_1/t2.biggest_value)*100) AS `%`
FROM myTable t1
JOIN (SELECT MAX(val_1) AS biggest_value,
project
FROM myTable
) AS t2
ON t2.project = t1.project
WHERE t1.project = $project_id
ORDER BY date DESC, id ASC
LIMIT 10
The JOIN gets the highest value on val_1 and the outer query is gathering all the data along with calculating the percentage of each val_1 based on the max value.
Live DEMO
Try like
SELECT val_1 as max_val FROM myTable ORDER by val_1 DESC LIMIT 1
You can also try with MAX like
SELECT MAX(val_1) as max_val FROM myTable
You may try this:
SELECT *, MAX(val_1) FROM (
SELECT *
FROM myTable
WHERE project = $project_id
ORDER BY date DESC
LIMIT 10) AS results ORDER by id ASC
Do something like;
SELECT MAX(val_1) FROM(SELECT *
FROM myTable
WHERE project = $project_id
ORDER BY date DESC
LIMIT 10) AS results ORDER by id ASC
See 3.6.1 The Maximum Value for a Column:
“What is the highest item number?”
SELECT MAX(article) AS article FROM shop;
You are free to use MAX() on query results as well:
SELECT MAX(val_1) FROM (SELECT …) t;
This query will give you the max with other columns : -
SELECT id,(select MAX(`price`) FROM `products` ) as FINAL from products
note :- This query assemble id and max price from the product table .i.e It associate max price with each record set.
I'm about to throw in the towel with this.
Preface: I want to make this work with any N, but for the sake of simplicity, I'll set N to be 3.
I've got a query (MySQL, specifically) that needs to pull in data from a table and sort based on top 3 values from that table and after that fallback to other sort criteria.
So basically I've got something like this:
SELECT tbl.id
FROM
tbl1 AS maintable
LEFT JOIN
tbl2 AS othertable
ON
maintable.id = othertable.id
ORDER BY
othertable.timestamp DESC,
maintable.timestamp DESC
Which is all basic textbook stuff. But the issue is I need the first ORDER BY clause to only get the three biggest values in othertable.timestamp and then fallback on maintable.timestamp.
Also, doing a LIMIT 3 subquery to othertable and join it is a no go as this needs to work with an arbitrary number of WHERE conditions applied to maintable.
I was almost able to make it work with a user variable based approach like this, but it fails since it doesn't take into account ordering, so it'll take the FIRST three othertable values it finds:
ORDER BY
(
IF(othertable.timestamp IS NULL, 0,
IF(
(#rank:=#rank+1) > 3, null, othertable.timestamp
)
)
) DESC
(with a #rank:=0 preceding the statement)
So... any tips on this? I'm losing my mind with the problem. Another parameter I have for this is that since I'm only altering an existing (vastly complicated) query, I can't do a wrapping outer query. Also, as noted, I'm on MySQL so any solutions using the ROW_NUMBER function are unfortunately out of reach.
Thanks to all in advance.
EDIT. Here's some sample data with timestamps dumbed down to simpler integers to illustrate what I need:
maintable
id timestamp
1 100
2 200
3 300
4 400
5 500
6 600
othertable
id timestamp
4 250
5 350
3 550
1 700
=>
1
3
5
6
4
2
And if for whatever reason we add WHERE NOT maintable.id = 5 to the query, here's what we should get:
1
3
4
6
2
...because now 4 is among the top 3 values in othertable referring to this set.
So as you see, the row with id 4 from othertable is not included in the ordering as it's the fourth in descending order of timestamp values, thus it falls back into getting ordered by the basic timestamp.
The real world need for this is this: I've got content in "maintable" and "othertable" is basically a marker for featured content with a timestamp of "featured date". I've got a view where I'm supposed to float the last 3 featured items to the top and the rest of the list just be a reverse chronologic list.
Maybe something like this.
SELECT
id
FROM
(SELECT
tbl.id,
CASE WHEN othertable.timestamp IS NULL THEN
0
ELSE
#i := #i + 1
END AS num,
othertable.timestamp as othertimestamp,
maintable.timestamp as maintimestamp
FROM
tbl1 AS maintable
CROSS JOIN (select #i := 0) i
LEFT JOIN tbl2 AS othertable
ON maintable.id = othertable.id
ORDER BY
othertable.timestamp DESC) t
ORDER BY
CASE WHEN num > 0 AND num <= 3 THEN
othertimestamp
ELSE
maintimestamp
END DESC
Modified answer:
select ilv.* from
(select sq.*, #i:=#i+1 rn from
(select #i := 0) i
CROSS JOIN
(select m.*, o.id o_id, o.timestamp o_t
from maintable m
left join othertable o
on m.id = o.id
where 1=1
order by o.timestamp desc) sq
) ilv
order by case when o_t is not null and rn <=3 then rn else 4 end,
timestamp desc
SQLFiddle here.
Amend where 1=1 condition inside subquery sq to match required complex selection conditions, and add appropriate limit criteria after the final order by for paging requirements.
Can you use a union query as below?
(SELECT id,timestamp,1 AS isFeatured FROM tbl2 ORDER BY timestamp DESC LIMIT 3)
UNION ALL
(SELECT id,timestamp,2 AS isFeatured FROM tbl1 WHERE NOT id in (SELECT id from tbl2 ORDER BY timestamp DESC LIMIT 3))
ORDER BY isFeatured,timestamp DESC
This might be somewhat redundant, but it is semantically closer to the question you are asking. This would also allow you to parameterize the number of featured results you want to return.
Here is my table structure
phone_calls(id, phone_number, call_id, timestamp, colx, col y )
I want to retrieve 10 most recent calls from phone_calls table within a group concat, without sub query
Try this (without a subquery it will not work):
SELECT
GROUP_CONCAT(call_id)
FROM (
SELECT
call_id
FROM
phone_calls
ORDER BY
id DESC
LIMIT 10
) as tmp
UPDATE: without sub-query:
SET #c:='';
SELECT
#c:=CONCAT(#c,',',call_id)
FROM
phone_calls
ORDER BY
id DESC
LIMIT 10;
SELECT #c;
Ok I'm not sure if this is your problem, but to use group_concat you need a group by column right? Not sure if this will work but you can try it, I used this dummy col way before
select 1 as col, group_concat(call_id) as latest_calls from phone_calls
ORDER BY timestamp DESC GROUP BY col LIMIT 10
you'll have an extra useless column col, but latest_calls should be correct
From the following four records, I want to select the OwnerId of second-latest record
ItemId OwnerId Date
11477 20981 2013-05-13
11477 1 2013-05-21
11477 21086 2013-05-22 #this is the one I'm talking about
11477 3868 2013-05-24
How to go about it?
This needs ItemID to be specified,
SELECT *
FROM TableName
WHERE ItemID = '11477'
ORDER BY DATE DESC
LIMIT 1,1
SQLFiddle Demo
However, if you don't want to specify the ItemID, and you want to get all second latest record for every ItemID, you can use a correlated subquery to generate a sequence number for every ItemID based on lastest DATE,
SELECT ItemId, OwnerID, Date
FROM
(
SELECT A.ItemId,
A.OwnerId,
A.Date,
(
SELECT COUNT(*)
FROM tableName c
WHERE c.ItemId = a.ItemId AND
c.Date >= a.Date) AS RowNumber
FROM TableName a
) x
WHERE RowNumber = 2
SQLFiddle Demo
select ownerid
from your_table
order by date desc
limit 1, 1
I think you can just to ORDER BY date descending, which will give you an order from newer to older, then LIMIT 1,1 to get only the second result, which should be the one you look for
SELECT *
FROM table
ORDER BY date DESC
LIMIT 1,1