Cannot subtract one value from another (unsigned) - mysql

I have a table stats with three columns:
id
up - number of upvotes (just like here at StackOverflow)
down - analogue to up
up and down are INT(11) and UNSIGNED (because they'll only be positive values).
Now when I want to fetch the ten items with the highest (up-down) value, I'm using this query:
SELECT id, up, down, (up-down) AS result
FROM stats
ORDER BY result DESC
LIMIT 0,10
But I'm getting an error
#1690 - BIGINT UNSIGNED value is out of range in
'(`database`.`stats`.`up` - `database`.`stats`.`down`)'
If I leave out the ORDER BY result DESC everything runs smoothly (except for the fact that they're not ordered by result, but the math of up-down is working).
What would I have to change in my query in order to retreive the correct result? Or do I have to remove the UNSIGNED attribute? But isn't this an appropriate case where I should use that attribute?

Unsigned remain unsigned, so you have a problem when the result would be negative. Cast to signed before the subtraction:
SELECT id, up, down, cast(up as signed) - cast(down as signed) AS result
FROM stats
ORDER BY result DESC
LIMIT 0, 10;
Or, keep your query and add a where clause:
SELECT id, up, down, (up-down) AS result
FROM stats
WHERE up >= down
ORDER BY result DESC
LIMIT 0,10;

Related

order by date and select until specific id is reached

I have a simple table with ID, STATUS, DATE columns, the rows in the table are ordered by DATE, I want to get all the rows until a specific ID is reached, and then stop, something like:
SELECT FROM myTable WHERE `DATE` <= '2017-10-09' ORDER BY `DATE` ASC UNTIL? `ID` = 119;
I like to know if that is possible somehow, to stop on a specific ID, whatever the ID was..
Thanks.
EDIT EXPLAINING
I want to select rows that are ordered under any column, but stop when a specific provided ID is reached. in the above image the result should be all the rows except the ones below the row 119.
I hope it's clear now.
Something like this might work:
SET #marker = NULL;
SELECT *
FROM myTable
WHERE `DATE` <= '2017-10-09'
AND ISNULL(#marker := IF(id = 119, 1, #marker))
ORDER BY `DATE` ASC;
You should phrase the query to select records whose date is less than the date of the record for which ID = 119:
SELECT *
FROM myTable
WHERE DATE <= (SELECT DATE FROM myTable WHERE ID = 119);
Tim Biegeleisen's answer is correct, but if you have no time in your date field then the order by is going to use the date and then any ordered indexes you have specified. So you will get all of the records back that equal the date of the id specified in the subquery's WHERE clause.
So for example, you are going to also return the rows for ids 200-202, no way around that unless you provide more precision on your date OR add the id field to your order by clause, in which case you'll need to be comfortable excluding all IDs above the specified ids integer value for the same date.

MySQL ORDER BY + LIMIT + OFFSET statements: how to OFFSET firts and only then sort with ORDER BY

I have a table which consists of ID, NAME, PRICE and DATE columns.
I'm trying to write a pager-style navigation because there are plenty of entries in the table, so looking at the whole SELECT's output had become uncomfortable.
I've written the following request:
SELECT id
, name
, price
, date
FROM mytable
ORDER
BY name asc
LIMIT 30 OFFSET _here's_my_offset_depending_on_the_current_position_
This works fine only as in the example. When I try to sort it, say, by price, it seems that MYSQL first sorts the whole table with ORDER BY and only after it makes an offset.
How do I change this behavior, in other words how do I make an offset and only than sort the resulting rows by whaterver I like?
It's can be easy if you use subquery:
SELECT * FROM (
SELECT id, name, price, date FROM mytable LIMIT 30 OFFSET _offset_
) AS page ORDER BY page.name asc

cast to unsigned need very long

I've the following problem:
i want to query some data from 3 big SQL-Tables.
eintraege ~13000 rows // rubrik2eintrag ~ 9500 rows // rubriken ~ 425 rows
This query
SELECT eintraege.id AS id, eintraege.email, eintraege.eintrags_name, eintraege.telefon,
eintraege.typ, rubrik2eintrag.rubrik AS rubrik, eintraege.status,
IFNULL( GROUP_CONCAT( rubriken.bezeichnung ), \'- Keine Rubrik zugeordnet\' ) AS rubrikname
FROM eintraege
LEFT OUTER JOIN rubrik2eintrag ON rubrik2eintrag.eintrag = eintraege.id
LEFT OUTER JOIN rubriken ON rubrik = rubriken.rubrik_id
GROUP BY id
ORDER BY `id` DESC
LIMIT 0, 50
works fine for me (~ 2 seconds response time) but the entrys appear not in the correct order. (e.g. the row with the id 500 came right before the row with id 3000 )
so i cast the id to unsigned. like this:
ORDER BY CAST(`id` AS UNSIGNED) DESC
But now the query needs nearly 40 seconds.
Is there a better/faster way to reach a correct ordered output?
Apparently, id is not defined as integer (or numeric) datatype. That would explain the ordering, where it's ordering by string value.
Some possibilities:
Introduce a new column in the table with integer datatype, populate/maintain the contents of that column, add an appropriate index with that column as the leading index, and change the query to order by the new column. (That would be the best MySQL approximation of a function based index.)
Or, store the string value with leading zeros, so they are the same length.
000000000500
000000030000
Or, redefine the id column to be integer type.
Aside from those ideas... no, there's really no getting around a Using filesort operation to order the rows by integer value.

Excluding first and last result from sql query

I'm fairly new to SQL so this may be fairly simple but I'm trying to write a script in SQL that will allow me to get a set of data but I don't want the first or last result in the query. I can find lots on how to remove the first result and how to remove the last result but not both.
This is my query so far:
SELECT * FROM itinerary Where ID = 'A1234' ORDER BY DateTime ASC
I want to remove the first and the last record of that select based on the DateTime.
This may not be the most performant way to do this, but you didn't give any schema info, and it looks like your ID column is not unique. It would be easier if you had a primary key to work with.
SELECT * FROM itinerary
WHERE ID = 'A1234'
AND DateTime <
(SELECT MAX(DateTime) FROM itinerary WHERE ID = 'A1234')
AND DateTime >
(SELECT MIN(DateTime) FROM itinerary WHERE ID = 'A1234')
ORDER BY DateTime ASC
This will basically select every record where the ID is A1234 and the DateTime doesn't equal the max or min datetime. Please note, if you have multiple records with the same value for DateTime and that also happens to be the min or max value, you might exclude more than just the first or last.
This might be good enough though. If not, you might need to write a stored procedure and not just straight ANSI SQL.
Try this ..
select * from
(select a.*,row_number() over (partition by DateTime order by DateTime desc) as rnm
from itinerary Where ID = 'A1234')x
where rm <> 1 and rm not in (
select max(rm) from
(
select row_number() over (partition by DateTime order by DateTime desc) as rnm
from itinerary Where ID = 'A1234'))
Select in reverse order & skip first and then select in the required order from the result, skipping first.
SELECT * FROM (SELECT *
FROM itinerary
Where ID = 'A1234'
ORDER BY DateTime DESC
LIMIT 1, 18446744073709551615) x ORDER BY DateTime ASC
LIMIT 1, 18446744073709551615
18446744073709551615 is max integer just in case you wanted to know why I picked that value

Sorting a number which is stored as varchar in mysql

I have this sql query:
SELECT * FROM `Fac__Invoice` ORDER BY `Fac__Invoice`.`invoice_number` ASC
The result is this table:
The problem is that number 10 is after 1. It should be after 9. This is because invoice_number is a varchar. I deliberately choose varchar because i want to store numbers like for example "FA001".
Can you write a sql query so that invoice_number 10 will be placed underneath 9?
Or is there any other solution?
Try to caste the value to unsigned:
SELECT * FROM `Fac__Invoice` ORDER BY CAST(`Fac__Invoice`.`invoice_number` AS UNSIGNED) ASC