mysql select with this query then pad out with this query? - mysql

I am trying to do a mysql query to select some news stories from a table, now the key is I always need 5 results.
so I was hoping to be able to pad out my results with another where clause ie
select * from here where this = 1
if there is < 5 results then select * from here where this = 2
limit [how ever many we are short say the first query brings back 3 results this should bring back 2]
Now I've looked at using a union to do this but without outside help ie from php and another query to count the results I don't think it is possible, I know I could simply use php to do this, and will probably end up doing that, but I was just wondering if what I am trying to do is possible with one mysql query?
EDIT:
also it needs to order by date but they are not really posted in order so
order by date get upto 5 where this = 1 and if there isn't 5 pad it out with the remainder of where this = 2 also ordered by date.
Another Shameful Edit:
ask a silly question lol... it was my sleep deprivation I just assumed there was data in the table and the previous coder was using unions to do all sorts of stuff, making me think it was more complex than it should be
SELECT *
FROM
news
WHERE
( this = 45 || this= 0 )
AND
active = '1'
ORDER BY
this ASC,
date_added DESC
LIMIT 5

How about -
SELECT *
FROM here
WHERE this < 5 -- added this WHERE clause based on the idea that there will be at least one item per this
ORDER BY this ASC, `date` ASC
LIMIT 5;
Or are you after the five results then being sorted by date again -
SELECT *
FROM (
SELECT *
FROM here
WHERE this < 5 -- added this WHERE clause based on the idea that there will be at least one item per this
ORDER BY this ASC, `date` ASC
LIMIT 5
) AS tmp
ORDER BY `date` ASC

You could combine the where clauses and use limit :
select * FROM here WHERE this = 1 OR this = 2 ORDER BY this LIMIT 5
Even in there were 15 records where this is equal to 1 this would only bring back 5 records ...

Related

SQL comparison within max function

I'm trying to get a list of 20 events grouped by their Ids and sorted by whether they are in progress, pending, or already finished. The problem is that there are events with the same id that include finished, pending, and in progress events and I want to have 20 distinct Ids in the end. What I want to do is group these events together but if one of them is in progress then sort that group by that event. So basically I want to sort by the latest end time that is also before now().
What I have so far is something like this where end and start are end/start times. I'm not sure if what is inside max() is behaving how I should expect.
select * from event_schedule as t1
JOIN (
SELECT DISTINCT(event_id) as e
from event_schedule
GROUP BY event_id
order by MAX(end < unix_timestamp(now())) asc,
MIN(start >= unix_timestamp(now())) asc,
MAX(start) desc
limit 0, 20
)
as t2 on (t1.event_id = t2.e)
This results in some running / pending events to be mixed around in order when I want them to be in the order running -> pending -> Ended.
I would suggest to first create a view in order to not get an overcomplicated SELECT statement:
CREATE VIEW v_event_schedule AS
SELECT *,
CASE
WHEN end < unix_timestamp(now())
THEN 1
WHEN start > unix_timestamp(now())
THEN 2
ELSE 3
END AS category
FROM event_schedule;
This view v_event_schedule returns an extra column, in addition to the columns of event_schedule, which represents the priority of the category (running, pending, past):
running (in progress)
pending (future)
past
Then the following will do what you want:
SELECT a.*
FROM v_event_schedule a
INNER JOIN (
SELECT id,
MIN(category) category
FROM v_event_schedule b
GROUP BY id
) b
ON a.id = b.id
AND a.category = b.category
ORDER BY category,
start DESC
LIMIT 20;
The ORDER BY can be further adapted to your needs as to how you want to sort within the same category. I added start DESC as that seemed what you were doing in your attempt.
About the original ORDER BY
You had this:
order by MAX(end < unix_timestamp(now())) asc,
MIN(start >= unix_timestamp(now())) asc,
The expressions you have there evaluate to boolean values, and both elements in the ORDER BY each divide the groups into two sections, one for false and one for true, so in total 4 groups.
The first of the two will order IDs first that have no record with an end value in the past, because only then the boolean expression is always false which is the only way to make the MAX of them false as well.
Now let's say for the same ID you have both records that have an end date in the future as well as records with an end date in the past. In that case the MAX aggregates to true, and so the id will be sorted secondary. This is not intended, as this ID might have a "running" record.
I did not look into making your query work based on such aggregates on boolean expressions. It requires some time to understand what they are doing. A CASE WHEN to determine the category with a number really makes the SQL a lot easier to understand, at least to me.

SQL: Skip entries in an order without knowing total entry amount

The title is a bit confusing, but I'm wondering if there is a way to do a query like this:
SELECT * FROM table ORDER BY timestamp LIMIT 10
and then only take the ones after the 10th one (or none if there are less than or equal to 10 entries).
EDIT I guess another way to do this would be to order them by timestamp, descending, and then somehow limit to 0, (total-someNumber).
By specifying an OFFSET you can get the rows after a specified number. You combine this with limit.
In MySQL you achieve this with LIMIT [offset], limit.
Example - get 10 records after the oldest 10 records:
SELECT * FROM table ORDER BY timestamp LIMIT 10, 10; # Retrieve rows 11-20
Example - get 20 records after the newest 5 records:
SELECT * FROM table ORDER BY timestamp DESC LIMIT 5, 20; # Retrieve rows 6-25
If you want to get ALL rows after a certain number (eg. 10) then you pass an arbitrarily big number for the limit since it is required by the clause:
SELECT * FROM table ORDER BY timestamp LIMIT 10,18446744073709551615; # Retrieve rows 11-BIGINT
Note: 18446744073709551615 is the maximum of an unsigned BIGINT and is provided as the solution within the MySQL documentation.
See:
http://dev.mysql.com/doc/refman/5.5/en/select.html
I'd try something like this and then just add a where clause that skips the first n (n=10 in this case) rows.
i.e. using the linked example:
SELECT
*
FROM
(select #n := #n + 1 RowNumber, t.* from (select #n:=0) initvars, tbl t)
WHERE
RowNumber > 10

How ORDER BY works in MySQL?

assume there is a table with 100 million records in it with this schema:
+---------+---------+------+
| user_id | post_id | date |
+---------+---------+------+
We want to have latest 30 records which user with #1 is posted.
SELECT * FROM users_posts WHERE user_id = 1 ORDER BY date DESC LIMIT 30
How MySQL search in table ? Does it search all rows ( full scan ) finding all matches THEN ordering them by date?
if Yes( Full Scan ), how can we edit this query to limit it to search latest records till finds 30 matches. ( to increase reading speed and have better performance )
thanks in advanced.
If you want the best performance for this query:
SELECT *
FROM users_posts
WHERE user_id = 1
ORDER BY date DESC;
Then you want an index on users_posts(user_id, date). This will allow the SQL engine to only use the index to define the set of rows being used. First, it will use user_id to satisfy the where clause. Then it will use the date part for the order by. It will still have to look up the records in the original data pages, in order to get all the columns needed for the select.
If you want to limit this to 30 rows, then add limit 30:
SELECT *
FROM users_posts
WHERE user_id = 1
ORDER BY date DESC
LIMIT 30;
With the above index, this will improve performance. With no index, the engine will have to scan all the data, sort the data that matches the where clause, and then apply the limit -- in other words, it won't be much of an improvement in performance.

Sqlite Query select statement with sorted result respecting the OFFSET

I want to make a sqlite query in such a way that the result should be sorted which has a LIMIT and the OFFSET. But the OFFSET should work in synch a manner that it should discard the last records from the result.
SELECT * FROM TempTable WHERE CLASS = 1 ORDER BY Date ASC LIMIT 100 OFFSET 5;
The above query just ignores the first 5 records from the table and give the remaining records. But instead I want it to ignore the first 5 latest entries.
Note:- the first 5 latest entries means since I am sorting it by date it should IGNORE the latest record inserted in the table respecting the date.
Sort backwards, with OFFSET 5 and resort again:
SELECT * FROM (
SELECT * FROM TempTable WHERE CLASS = 1 ORDER BY Date DESC LIMIT 100 OFFSET 5
) ORDER BY Date ASC;

Combine two SELECTS from same table

I have an table with projects in them:
id (int)
ordering (int)
content (string)
My projects-page shows the current project and a preview of the next three projects. For fetching the next three projects, I would like to use a clean MySQL query. Of course, if the person is on one of the last projects, there are not 3 more projects left to show.
So it then needs to show the first one again.
Basically Im trying to combine these two statements:
SELECT *
FROM projects
WHERE ordering > {currentProjectOrdering}
ORDER BY ordering ASC
and
SELECT *
FROM projects
WHERE ordering > 0
ORDER BY ordering ASC
and also
LIMIT 0,3
In a nutshell: get the next three records with higher ordering than the current project, if (some of) these do not exist, start from ordering = 1.
Assuming there are 10 projects:
Project 1 shows 2,3 and 4
Project 2 shows 3,4 and 5
...
Project 9 shows 10, 1 and 2
If you want data from first condition AND second condition use
SELECT *
FROM projects
WHERE ordering > {currentProjectOrdering} AND ordering > 0
ORDER BY ordering ASC
LIMIT 0,3
If you want data from first condition OR second condition use
SELECT *
FROM projects
WHERE ordering > {currentProjectOrdering} OR ordering > 0
ORDER BY ordering ASC
LIMIT 0,3
I'm curious about this. I don't have mysql and can't test, but I don't think you are going to get the 3 records you want in these examples - if you are at the end of the list and need to grab 1 or 2 from the beginning. I'm playing with TSQL - so there is no LIMIT Hence the TOP.
I'm also curious if anyone can do it better.
SELECT TOP 3
CASE
WHEN ORDERING >= {currentProjectOrdering} THEN ORDERING
ELSE {currentProjectOrdering} - ORDERING END AS DISPLAYORDER,*
FROM SO_projects
WHERE (ordering >= {currentProjectOrdering} AND ordering < {currentProjectOrdering}+ 3 ) OR ordering < 3
ORDER BY DISPLAYORDER DESC
Can't promise as I have no idea about MYSQL, but here is my try. In T-SQL TOP returns the specified number of recs (3 in this case) depending on the ORDER BY. There is no equivocation to the 0 in the 0,3 part (I wish) of LIMIT. I googled and it looks like the CASE stuff should be pretty similar. You may be able to get rid of the > and < because of how LIMIT works. The logic works on SqlServer:
SELECT
CASE
WHEN ORDERING >= {currentProjectOrdering} THEN ORDERING
ELSE {currentProjectOrdering} - ORDERING END AS DISPLAYORDER,*
FROM SO_projects
WHERE (ordering >= {currentProjectOrdering} AND ordering < {currentProjectOrdering} + 3 ) OR ordering < 3
ORDER BY DISPLAYORDER DESC
LIMIT 0,3
Something Like this should work (assuming you limiting by just projects that person is working on):
SELECT * FROM projects WHERE person = person_id && (ordering > 0 && COUNT(id) < 4)
|| (ordering >= currentProjectOrdering && COUNT(id) >= 4)) ORDER BY ordering LIMIT 0,3;