I have a blog SQL system with 'posts' table like this (id, theme, title, content, created).
I would like to get the last 3 titles of each post theme, to display in a table, but I didn't get it.
I tried this SQL code:
SELECT *
FROM posts
GROUP BY theme
ORDER BY created DESC
LIMIT 0,3
But this gives me the last 3 posts of the table!!!
Can you please help me?
Thanks in advance.
You're doing some parts right, but in others you're not understanding the use of the GROUP BY clause.
The LIMIT above works on the whole query results. Besides you need an aggregate function for the GROUP BY to be applied on, e.g. SUM, AVG, etc. You cannot simply SELECT * and expect grouping by theme.
The GROUP BY returns one row per item you're grouping by, for instance, the latest, and not three rows.
Therefore you need to perform the query per theme you're grouping by, but in this case you don't need to group by at all. Write a loop that executes this query for each theme you require:
SELECT *
FROM posts
WHERE theme = "your-theme"
ORDER BY created DESC
LIMIT 0,3
Josvic Zammit's answer is in parts right (+1 from me) but you don't have to perform the query per theme. This is a way you can do it in MySQL, although it would surely be easier to "perform the query per theme" in PHP or whatever.
SELECT * FROM
(
SELECT
IF(#prev != sq.theme, #rownum:=1, #rownum:=#rownum + 1) AS rownumber, #prev:=sq.theme, sq.*
FROM
(
SELECT
*
FROM
posts
, (SELECT #rownum:=0, #prev:=0) r
ORDER BY theme, created DESC
) sq
) q
WHERE q.rownumber <= 3
Note that I unfortunately can't test it right now, but it should work. Also note, that this query might be slower than performing a simpler query per theme.
Related
let's say I have a table CData with the columns CName, Amount1, Amount2.
Now I want to use a query to get calculate the difference between Amount1 and Amount2 for each distinct CName and, as a result of the query, get the ~1000 rows with the biggest difference and the 1000~ rows with the smallest (or most negative) difference. It doesn't matter if the results come in one table or two.
1) I am aware of the function TOP and so I could do this with two queries and sort by Difference (once ascending, once descending). Is there a way to do this in one query, though? This would save some time.
2) General question: When I define a field in my query (in this example "Difference"), can I somehow use it to, for example, sort the data by it? Like this (well, it's not working, but to give you an idea of what I mean):
SELECT CData.CName, CData.Amount2-CData.Amount1 AS Difference
FROM CData
GROUP BY CData.CName
ORDER BY Difference
Or do I always have to do the following:
...
ORDER BY CData.Amount2-CData.Amount1
Not much of a difference in this example, I just wanted to know if that's possible in general.
Sort the first time ASC (Ascending) and the second time DESC (Descending)
SELECT TOP 1000
CData.CName,
CData.Amount2 - CData.Amount1 AS Difference
FROM CData
GROUP BY CData.CName
ORDER BY CData.Amount2 - CData.Amount1 ASC
SELECT TOP 1000
CData.CName,
CData.Amount2 - CData.Amount1 AS Difference
FROM CData
GROUP BY CData.CName
ORDER BY CData.Amount2 - CData.Amount1 DESC
which aggregate functino do you want to perform for your differences? Avg? Sum?
SELECT CName, avg(Amount2-Amount1) AS Difference
FROM CData
GROUP BY CName
btw, to do it in 'one' query, you could use a union query on two subqueries, one with the TOP 1000 asc, one with the TOP 1000 desc
looks like Access is not allowing you to use an alias in the ORDER BY Clause, if you use the QBE grid you can change the format from the UI to SQL and it repeats the calculation in the ORDER BYclause.
Hi, John.
Check out the SO tour for instructions on how to use options such as formatting code.
Not sure if this will work for you, but you can try something like:
select * from
(SELECT TOP 3
CName, Date_Sale, Sum(Amount) AS SumA, 99999-Sum(Amount) as srt
FROM
Data
GROUP BY
CName, Date_Sale
UNION
SELECT TOP 3
CName, Date_Sale, Sum(Amount) AS SumA, Sum(Amount) as srt
FROM
Data
GROUP BY
CName, Date_Sale) u
order by
srt
I try to get 10 random rows of a table. Many similar questions are out there but I have got an additional condition: I want this list to update weekly, 52 times a year. These 10 items are going to be the "random featured ones of this week."
So, I found this snippet of getting random rows that change each time you invoke the script
select top 10 percent * from [yourtable] order by newid();
The problem with it is that I can't simply add a seed, which in this case would be the week-of-year. Any idea how to implement this?
Your code sample is T-SQL (SQL Server). So, to do this in SQL Server is pretty easy:
select *
from (select t.*,
row_number() over (partition by datepart(wk, thedate) order by newid()) as seqnum
from [yourtable] t
) t
where seqnum <= 10;
MySQL does not support row_number(), so the approach would be different in that database.
I have the following query I use and it works great:
SELECT * FROM
(
SELECT * FROM `Transactions` ORDER BY DATE DESC
) AS tmpTable
GROUP BY Machine
ORDER BY Machine ASC
What's not great, is when I try to create a view from it. It says that subqueries cannot be used in a view, which is fine - I've searched here and on Google and most people say to break this down into multiple views. Ok.
I created a view that orders by date, and then a view that just uses that view to group by and order by machines - the results however, are not the same. It seems to have taken the date ordering and thrown it out the window.
Any and all help will be appreciated, thanks.
This ended up being the solution, after hours of trying, apparently you can use a subquery on a WHERE but not FROM?
CREATE VIEW something AS
SELECT * FROM Transactions AS t
WHERE Date =
(
SELECT MAX(Date)
FROM Transactions
WHERE Machine = t.Machine
)
You don't need a subquery here. You want to have the latest date in the group of machines, right?
So just do
SELECT
t.*, MAX(date)
FROM Transactions t
GROUP BY Machine
ORDER BY Machine ASC /*this line is obsolete by the way, since in MySQL a group by automatically does sort, when you don't specify another sort column or direction*/
A GROUP BY is used together with a aggregate function (in your case MAX()) anyway.
Alternatively you can also specify multiple columns in the ORDER BY clause.
SELECT
*
FROM
Transactions
GROUP BY Machine
ORDER BY Date DESC, Machine ASC
should give you also what you want to achieve. But using the MAX() function is definitely the better way to go here.
Actually I have never used a GROUP BY without an aggregate function.
I've been doing a lot of reading on alternatives to the LIMIT clause for SQL SERVER. It's so frustrating that they still refuse to adapt it. Anyway, I really havn't been able to get my head around this. The query I'm trying to convert is this...
SELECT ID, Name, Price, Image FROM Products ORDER BY ID ASC LIMIT $start_from, $items_on_page
Any assistance would be much appreciated, thank you.
In SQL Server 2012, there is support for the ANSI standard OFFSET / FETCH syntax. I blogged about this and here is the official doc (this is an extension to ORDER BY). Your syntax converted for SQL Server 2012 would be:
SELECT ID, Name, Price, Image
FROM Products
ORDER BY ID ASC
OFFSET (#start_from - 1) ROWS -- not sure if you need -1
-- because I don't know how you calculated #start_from
FETCH NEXT #items_on_page ROWS ONLY;
Prior to that, you need to use various workarounds, including the ROW_NUMBER() method. See this article and the follow-on discussion. If you are not on SQL Server 2012, you can't use standard syntax or MySQL's non-standard LIMIT but you can use a more verbose solution such as:
;WITH o AS
(
SELECT TOP ((#start_from - 1) + #items_on_page)
-- again, not sure if you need -1 because I
-- don't know how you calculated #start_from
RowNum = ROW_NUMBER() OVER (ORDER BY ID ASC)
/* , other columns */
FROM Products
)
SELECT
RowNum
/* , other columns */
FROM
o
WHERE
RowNum >= #start_from
ORDER BY
RowNum;
There are many other ways to skin this cat, this is unlikely to be the most efficient but syntax-wise is probably simplest. I suggest reviewing the links I posted as well as the duplicate suggestions noted in the comments to the question.
For SQL Server 2005 and 2008
This is an example query to select rows from 11 to 20 from Report table ordered by LastName.
SELECT a.* FROM
(SELECT *, ROW_NUMBER() OVER (ORDER BY LastName) as row FROM Report) a
WHERE a.row > 10 and a.row <= 20
Try this:
SELECT TOP $items_on_page ID, Name, Price, Image
FROM (SELECT TOP $start_from + $items_on_page - 1 * FROM Products ORDER BY ID) as T
ORDER BY ID DESC
EDIT: Explanation-
No getting around the subquery, but this is an elegant solution.
Say you wanted 10 items per page, starting on the 5th row, this would give you the bottom 10 rows of the top 14 rows. Essentially LIMIT 5,10
you can use ROW COUNT : Returns the number of rows affected by the last statement. when you don, you reset the rowcont.
SET ROWCOUNT 100
or
you can try using TOP query
SELECT TOP 100 * FROM Sometable ORDER BY somecolumn
If you allow the application to store a tad of state, you can do this using just TOP items_on_page. What you do is to store the ID of the last item you retrieved, and for each subsequent query add AND ID > [last ID]. So you get the items in batches of items_on_page, starting where you left off each time.
I have a query to fetch the top 50 entries based on a total_score field. From these top 50, I need to be able to order them by any of their other fields. I tried using multiple ORDER BY statements, but the result set never changes. Here's my query:
SELECT
e.id, e.name , e.total_score
FROM
entry e
ORDER BY
e.total_score DESC, e.name ASC
I think I understand why this doesn't do what I need, so I suppose my question is how can I achieve my goal? In the end I always need the 50 entries with the highest total_score, but this selection should be then orderable by any other field.
If this isn't possible in MySQL, I can do it with PHP, but I'd rather let the DB handle this.
Note: I'm using Doctrine 1.2 on top of a MySQL 5 DB.
Thanks!
ORDER BY, when used with multiple columns, only uses the subsequent columns in case of tie in the previous ones.
Try using nested queries
SELECT * FROM(
SELECT TOP 50
e.id, e.name , e.total_score, e.(YOURFIELD)
FROM
entry e
ORDER BY
e.total_score DESC ) x ORDER BY x.(YOURFIELD)
You need a subquery
select * from (
select ..... order by .... limit 50
) as t
order by something_else