Числа с плавающей запятой, хранящиеся в компьютере, состоят из двух частей: целого и экспоненты, в которых база берется и умножается на целую часть.
Если компьютер работал в базе 10, 0.1
будет 1 x 10⁻¹
, 0.2
будет 2 x 10⁻¹
, а 0.3
будет 3 x 10⁻¹
. Целочисленная математика проста и точна, поэтому добавление 0.1 + 0.2
, очевидно, приведет к 0.3
.
Компьютеры обычно не работают в базе 10, они работают в базе 2. Вы все равно можете получить точные результаты для некоторые значения, например 0.5
, равны 1 x 2⁻¹
, а 0.25
- 1 x 2⁻²
, а их добавление приводит к 3 x 2⁻²
или 0.75
. Точно.
Проблема связана с числами, которые могут быть представлены точно в базе 10, но не в базе 2. Эти цифры должны округляться до их ближайшего эквивалента. Предполагая, что для 64-битного формата с плавающей точкой IEEE используется очень общий формат, ближайшим номером к 0.1
является 3602879701896397 x 2⁻⁵⁵
, а ближайшим номером к 0.2
является 7205759403792794 x 2⁻⁵⁵
; добавление их результатов в 10808639105689191 x 2⁻⁵⁵
или точное десятичное значение 0.3000000000000000444089209850062616169452667236328125
. Номера с плавающей запятой, как правило, округлены для отображения.
С помощью Postgres 9.4 это можно сделать немного короче:
select c.*
from comments c
join (
select *
from unnest(array[43,47,42]) with ordinality
) as x (id, ordering) on c.id = x.id
order by x.ordering
Удаление необходимости вручную назначать / поддерживать позицию для каждого значения.
С помощью Postgres 9.6 это можно сделать, используя array_position()
:
with x (id_list) as (
values (array[42,48,43])
)
select c.*
from comments c, x
where id = any (x.id_list)
order by array_position(x.id_list, c.id);
CTE используется, так что список значений должен быть только указанный один раз. Если это не важно, это также можно записать в виде:
select c.*
from comments c
where id in (42,48,43)
order by array_position(array[42,48,43], c.id);
В Postgresql:
select *
from comments
where id in (1,3,2,4)
order by position(id::text in '1,3,2,4')
В Postgres 9.4 или новее это, вероятно, простейшее и быстрое :
SELECT c.*
FROM comments c
JOIN unnest('{1,3,2,4}'::int[]) WITH ORDINALITY t(id, ord) USING (id)
ORDER BY t.ord;
WITH ORDINALITY
что @a_horse уже упоминается . Подробное объяснение:
SELECT * FROM "comments" JOIN (
SELECT 1 as "id",1 as "order" UNION ALL
SELECT 3,2 UNION ALL SELECT 2,3 UNION ALL SELECT 4,4
) j ON "comments"."id" = j."id" ORDER BY j.ORDER
, или если вы предпочитаете зло над добром:
SELECT * FROM "comments" WHERE ("comments"."id" IN (1,3,2,4))
ORDER BY POSITION(','+"comments"."id"+',' IN ',1,3,2,4,')
Просто потому, что его так сложно найти, и его нужно распространять: в mySQL это можно сделать гораздо проще , но я не знаю, работает ли он в другом SQL.
SELECT * FROM `comments`
WHERE `comments`.`id` IN ('12','5','3','17')
ORDER BY FIELD(`comments`.`id`,'12','5','3','17')
create sequence serial start 1;
select * from comments c
join (select unnest(ARRAY[1,3,2,4]) as id, nextval('serial') as id_sorter) x
on x.id = c.id
order by x.id_sorter;
drop sequence serial;
[EDIT]
unsest еще не встроен в 8.3, но вы можете создать его самостоятельно (красота любого *):
create function unnest(anyarray) returns setof anyelement
language sql as
$$
select $1[i] from generate_series(array_lower($1,1),array_upper($1,1)) i;
$$;
эта функция может работать в любом типе:
select unnest(array['John','Paul','George','Ringo']) as beatle
select unnest(array[1,3,2,4]) as id
И вот другое решение, которое работает и использует постоянную таблицу ( http://www.postgresql.org/docs/8.3/interactive/sql-values.html ):
SELECT * FROM comments AS c,
(VALUES (1,1),(3,2),(2,3),(4,4) ) AS t (ord_id,ord)
WHERE (c.id IN (1,3,2,4)) AND (c.id = t.ord_id)
ORDER BY ord
Но опять же я не уверен, что это исполнитель.
Теперь у меня есть куча ответов. Могу ли я получить голосование и комментарии, чтобы я знал, кто победитель!
Спасибо всем :-)
Чтобы сделать это, я думаю, что у вас, вероятно, должна быть дополнительная таблица «ORDER», которая определяет сопоставление идентификаторов на заказ (эффективно делая то, что ответ на ваш собственный вопрос), которые затем можно использовать в качестве дополнительного столбца на ваш выбор, который вы затем можете сортировать.
Таким образом, вы явно описываете упорядочение, которое вы желаете в базе данных, где оно должно быть.
Я думаю, что этот способ лучше:
SELECT * FROM "comments" WHERE ("comments"."id" IN (1,3,2,4))
ORDER BY id=1 DESC, id=3 DESC, id=2 DESC, id=4 DESC
Другим способом сделать это в Postgres будет использование функции idx
.
SELECT *
FROM comments
ORDER BY idx(array[1,3,2,4], comments.id)
Не забудьте сначала создать функцию idx
, как описано здесь: http://wiki.postgresql.org/wiki/Array_Index
Я согласен со всеми другими плакатами, которые говорят «не делай этого», или «SQL не хорош в этом». Если вы хотите отсортировать по некоторым аспектам комментариев, добавьте еще один целочисленный столбец в одну из ваших таблиц, чтобы сохранить критерии сортировки и отсортировать их по этому значению. например, «ORDER BY comments.sort DESC» Если вы хотите сортировать их в другом порядке каждый раз, тогда ... SQL не будет для вас в этом случае.
Небольшое улучшение по сравнению с версией, которая использует последовательность, я думаю:
CREATE OR REPLACE FUNCTION in_sort(anyarray, out id anyelement, out ordinal int)
LANGUAGE SQL AS
$$
SELECT $1[i], i FROM generate_series(array_lower($1,1),array_upper($1,1)) i;
$$;
SELECT
*
FROM
comments c
INNER JOIN (SELECT * FROM in_sort(ARRAY[1,3,2,4])) AS in_sort
USING (id)
ORDER BY in_sort.ordinal;
select * from comments where comments.id in
(select unnest(ids) from bbs where id=19795)
order by array_position((select ids from bbs where id=19795),comments.id)
здесь, [bbs] - основная таблица с полем, называемым ids, и, ids - это массив, в котором хранятся комментарии .id.
передано в postgresql 9.6