web-dev-qa-db-ja.com

SQL:「DISTINCTON(式)」は何をしますか?

DISTINCTがどのように機能するかは理解していますが、DISTINCT ON (expression)は理解していません。

このスクリーンショットの最初の例を見てください。

enter image description here

(a % 2)一部はすべてに影響しますか? a % 2はtrueと評価され、それを返し、他のすべてのタプルについてもそうし続けますが、戻り値が異なる場合にのみ戻りますか?

6
JohnSmithy1266

前の答えは正しいように見えますが、特に明確だとは思いません。

PostGreSQLの 公式ドキュメント のスニペットは次のとおりです...

DISTINCT ON(式[、...])は、指定された式が等しいと評価される行の各セットの最初の行のみを保持します。 [...] ORDER BYを使用して目的の行が最初に表示されるようにしない限り、各セットの「最初の行」は予測できないことに注意してください。 [...] DISTINCT ON式は、左端のORDERBY式と一致する必要があります。

最初のポイントは、ON ()に入れたものはすべて、_ORDER BY_の最初に来る必要があるということです。理由は、すぐに明らかになることを願っています...

_SELECT DISTINCT ON (a) a, b, c FROM a_table ORDER BY a, b
_

次に、結果がフィルタリングされるため、個別のエンティティごとに、最初の行のみが実際に返されます。


例えば...

_CREATE TABLE example (
    id               INT,
    person_id        INT,
    address_id       INT,
    effective_date   DATE
);

INSERT INTO
    example (id, person_id, address_id, effective_date)
VALUES
    (1, 2, 1, '2000-01-01'),  -- Moved to first house
    (5, 2, 2, '2004-08-19'),  -- Went to uni
    (9, 2, 1, '2007-06-12'),  -- Moved back home

    (2, 4, 3, '2007-05-18'),  -- Moved to first house
    (3, 4, 4, '2016-02-09')   -- Moved to new house
;

SELECT DISTINCT ON (person_id)
    *
FROM
    example
ORDER BY
    person_id,
    effective_date DESC
;
_

これにより、各人のすべてのレコードが連続するように結果が並べ替えられ、最新のレコードから古いレコードの順に並べられます。次に、各人について、最初のレコードが返されます。したがって、各人の最新の住所を指定します。

_Step 1 : Apply the ORDER BY...

 id | person_id | address_id | effective_date
----+-----------+------------+----------------
  9 |      2    |      1     |  '2007-06-12'
  5 |      2    |      2     |  '2004-08-19'
  1 |      2    |      1     |  '2000-01-01'
  3 |      4    |      4     |  '2016-02-09'
  2 |      4    |      3     |  '2007-05-18'

Step 2 : filter to just the first row per person_id

 id | person_id | address_id | effective_date
----+-----------+------------+----------------
  9 |      2    |      1     |  '2007-06-12'
  3 |      4    |      4     |  '2016-02-09'
_


これは、以下とほぼ同等です...

_SELECT
    *
FROM
(
    SELECT
        *,
        ROW_NUMBER() OVER (PARTITION BY person_id
                               ORDER BY effective_date DESC)  AS person_address_ordinal
    FROM
        example
)
    AS sorted_example
WHERE
    person_address_ordinal = 1
_


_(a % 2)_が何をするかについての質問に関しては、それはMOD(a, 2)の単なる数学計算なので、次のことができます...

_CREATE TABLE example (
    id               INT,
    score            INT
);

INSERT INTO
    example (id, score)
VALUES
    (1, 2),
    (2, 6),
    (3, 5),
    (4, 3),
    (5, 4),
;

SELECT DISTINCT ON (id % 2)
    *
FROM
    example
ORDER BY
    id % 2,
    score DESC
;
_

これにより、偶数のids(_id % 2_は_0_)に最高のスコアが与えられ、次に最高のスコアは奇数のids(ここで、_id % 2_は_1_)と同じです。

_Step 1 : Apply the ORDER BY...

 id | score
----+-------

  2 |   6     -- id % 2 = 0
  4 |   3     -- id % 2 = 0

  3 |   5     -- id % 2 = 1
  5 |   4     -- id % 2 = 1
  1 |   2     -- id % 2 = 1

Step 2 : filter to just the first row per `id % 2`

 id | score
----+-------
  2 |   6     -- id % 2 = 0
  3 |   5     -- id % 2 = 1
_
12
MatBailie

%2はモジュロ演算子です。 0または1しか取得できません(列がNULL可能の場合はNULL)。

例えば:

 i    |   a   | a%2
 1        10     0
 2        11     1
 3        12     0 
 4        13     0

コード:

CREATE TABLE r(i INT, a INT);
INSERT INTO r(i, a)  VALUES (1,10), (2,11),(3,12),(4,13);

SELECT DISTINCT ON (a%2) a
FROM r;

出力:

10
11

SELECT DISTINCT ON (a%2) a
FROM r
ORDER BY a%2,i DESC;

出力:

12
13

Rextester Demo

1
Lukasz Szozda