How to make MySQL group by choose one value before another? - mysql

size color in_stock
----- ----- -----
small red 0
large red 1
xlarge red 1
When I'm using GROUP BY size,color, the first row with in_stock 0 is being chosen over the second row. Is there any way to have GROUP BY always give priority for rows with in_stock 1, rather than in_stock 0?

The short answer is: No
I suspect (because you don't supply your original query) that you are using something like this:
SELECT size,color, in_stock
FROM atable
GROUP BY size,color
MySQL allows a GROUP BY clause to have just a few non-aggregating columns - BUT it only does so by virtue of a server setting
see: http://dev.mysql.com/doc/refman/5.0/en/group-by-handling.html
If you use this "feature" there is no control over what data is chosen in the other non-aggregating columns.
You should NOT use this "feature" of MySQL because if the server settings turn off this extension your queries will no longer work.
You could do something like this instead:
SELECT size,color, MIN(case when in_stock = 1 then in_stock else NULL end)
FROM atable
GROUP BY size,color

You can use syntax as per below
select size, color, in_stock, ....other fields...
from yourtable
where ...conditions if any....
group by size,color
order by in_stock desc;
Order by always work after group by, so if you want to first order then group. you can use below query-
select size, color, in_stock
from
(
select size, color, in_stock, ....other fields...
from yourtable
where ...conditions if any....
order by in_stock desc
) as a
group by size,color;

If you GROUP BY column(s) and also return a non aggregate column that is not in the GROUP BY clause, then which row that columns value is taken from is not defined. It might be the first one, it might be the last one. It might change depending on storage engine, or anything else.
If you specifically wanted the 2nd one you could do something like this:-
SELECT size, color, SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(in_stock ORDER BY FIELD(size, 'small', 'large', 'xlarge')), ',', 2), ',', -1)
FROM atable
GROUP BY size,color

Related

MySQL UNION SELECT statement order by two different columns

I have a table with a column called "myorder". I want to only order the first 5 elements in ascending order by using the column "myorder" and then the rest of the elements ordererd by id in descending order. I've tried but I don't seem to get this 100%. I have created a sqlfiddle with this simple example. Can anybody help me?
The order in the sqlfiddle should be: Blue, Brown, Yellow, Red, Green, Orange, Gray, Black, White.
maybe try
SELECT * from tbl ORDER BY IF(myorder=0, 2147483647, myorder) ASC, id DESC
if by "first 5" you mean ones with nonzero myorder
Append this to the end of your sql statement:
ORDER BY FIELD(id, 3,4,5,1,2,9,8,7,6)
This will manually sort the results based on the id field in the specified order getting the colours in order.
Alternatively you could:
ORDER BY FIELD(NAME, BLUE,BROWN,YELLOW......)
Union does not preserves order, at least not in all databases.
To get this done you need to select a fake column and use order on the results (total)
Here is a solution:
(
SELECT Id, myorder, myorder as fake, name from tbl WHERE myorder!=0) UNION
(SELECT id, myorder, 2000 as fake,name from tbl WHERE myorder=0) ORDER BY fake asc, id desc
This Will work in any case, as long as you keep the fake number high enough.
Can we have NULL value instead of zero when myorder is not specified?
If so we can do this:
SELECT * from tbl ORDER BY -myorder DESC, id DESC
Fiddle:
http://sqlfiddle.com/#!2/ef9ee/4
Hint on forcing nulls to sort last: MySQL Orderby a number, Nulls last
Use a CASE statement and the following trick (for example):
SELECT *,
CASE myorder
WHEN 0 THEN id
ELSE 99999999 - myorder
END AS ord
FROM tbl
ORDER BY ord DESC
SQL Fiddle

Advanced sorting in mysql

I have a table with a column containing productnames.
I want to sort it by productname ascending, BUT i want the productnames containing the word "accessory" to be sorted LAST.
How can this be done?
This is a common way to do this:
SELECT * FROM my_table
ORDER BY product_name LIKE '%accessory%', product_name
or
SELECT * FROM my_table
ORDER BY LOCATE('accessory', product_name), product_name
The clause product_name LIKE '%accessory%' is a Boolean expression, which returns 0 or 1, so all the rows that don't match will return 0 and will appear earlier in the sort order.
LOCATE('accessory', product_name) works similarly, but will return 0 for no match or the integer location of the first match. In this case, there's little difference between the two.
SELECT *,
(CASE WHEN `productname` LIKE '%accessory%' THEN 1 ELSE 0 END) AS `relevance`
FROM `tablename` ORDER BY `relevance`, `productname` ASC
You can give relevance based on conditions in mysql using case.
Obviously you can add your WHERE clause in there just before the ORDER BY statement, but for this example I just select everything

How to order your result in a specific way in SQL Server

I have a simple query, but I would like to see the results in a specific way. I would like to see 'N/A' at the top of the result, without having to result to "Case When Then"
Select *
From Ordertype
Results:
Car21
Car34
Bus42
N/A
Thanks,
There are no 'overrides' for ORDER BY, if you want the specific order you're asking for, you'll have to use the CASE:
SELECT type
FROM OrderType
ORDER BY
CASE
WHEN type = 'N/A' THEN 1
ELSE 2
END
,type
If you want an arbitrary order that is not tied directly to the structured of a column (alphabetical/numerical) but rather to it's importance which only you know in your head it can be useful to add a Rank column to your table.
Column1 Rank
Car21
Car34 2
Bus42 1
N/A 99
then you can do
select Column1
from Table
order by rank desc, column1
This will put highly ranked items first then low ranked items, then when rows don't have a rank it will sort them alphabetically by column1
You can try this:
SELECT * FROM ordertype ORDER BY ID DESC
to see the newest ones 1st

Row number in mySQL

Is it possible to get the row number in MySQL? Say I have a 'table'
ID tag name
1 A alpha
4 B beta
5 C gamma
8 D ceta
How can I get in MySQL that, for example, 'C' is the 3rd row in that table? Following:
SET #pos=0;
SELECT #pos:=#pos+1,tag FROM table ORDER BY tag ASC;
counts the rows as it should. But (sorry for ignorant code)
SET #pos=0;
SELECT #pos:=#pos+1,tag FROM table where tag='C' ORDER BY tag ASC;
gives 1 row as a result, with pos as 0, as it probably should.
Is there a way to get the 'pos' to be '3' as I need it to be?
(Ordering would be important as well, whether it is relevant to the question or not..)
You can use this
Select rownum from (
SELECT #rownum:=#rownum+1 rownum, t.*FROM (SELECT #rownum:=0) r, table t order by tag) as t1
where tag = 'C'
In case your IDs are strictly increasing with row numbers, you can do
SELECT COUNT(*) FROM tbl WHERE ID <= (SELECT ID FROM tbl WHERE tag = 'C');
I am not sure what you mean by the ordering though.
Side note: Your code
SET #pos=0;
SELECT #pos:=#pos+1,tag FROM tbl where tag='C' ORDER BY tag ASC;
cant work, because here #pos operates on the result set only, which consists of only one record.
Actually, there's one possibility that I didn't consider before:
SELECT count(1) FROM table WHERE tag <= 'C' ORDER BY tag
This seems to do the same thing, and a bit faster, too.. Or am I missing something?

SQL - Order By Field in Random Numeric Order

I have a SQL query which I need to use to order a field. Typically a query would go something like this in hibernate
select * from model order by model.field1, model.field2
The issue is that the first sort needs to be done in a very specific order.
Where the order in field1 has to be filtered in a SPECIFIC order.
So instead of listing cars by an alphabetical order for example. I would prefer to sort in say
Lexus, Ford, Toyota, Mazda, Mercedes etc. Is there a clean way to do this? Currently I get the result and then have to put it in specific lists and is just not very clean. I do not have option of modifying things in the database
Thanks.
You can create a custom ORDER BY using a CASE statement.
The CASE statement checks for your condition and assigns to rows which meet that condition a lower value than that which is assigned to rows which do not meet the condition.
It's probably easiest to understand given an example:
SELECT mycolumn
FROM model
ORDER BY CASE WHEN model.field1 = 'Lexus' THEN 0
WHEN model.field1 = 'Ford' THEN 1
WHEN model.field1 = 'Toyota' THEN 2
WHEN model.field1 = 'Mazda' THEN 3
WHEN model.field1 = 'Mercedes' THEN 4 END, model.field2;
order by (case
when field1 = 'Lexus' then 1
when field1 = 'Ford' then 2
when field1 = 'Toyota' then 3
...
else null end)
You need to use window functions and the PARTITION BY clause like so:
SELECT ROW_NUMBER() OVER(PARTITION BY manufacturer ORDER BY field1) [RN],
manufacturer,
field1,
field2
FROM model
This will give a row number for each different grouping in the partition by clause based on the ordering of field1 in this example.
EDIT:
If your RDMS does not support window functions, you can order by manufacturer then a second value - this would 'group' all the rows for each manufacturer together:
SELECT manufacturer, field1
FROM model
ORDER BY manufacturer, field1