web-dev-qa-db-fra.com

MAX () OVER PARTITION BY dans Oracle SQL

J'essaie d'utiliser la fonction MAX () OVER PARTITION BY pour évaluer le dernier reçu d'une pièce spécifique achetée par ma société. Vous trouverez ci-dessous un exemple de tableau d’informations pour quelques parties de l’année écoulée:

| VEND_NUM | VEND_NAME    | RECEIPT_NUM | RECEIPT_ITEM | RECEIPT_DATE |
|----------|--------------|-------------|----------|--------------|
| 100      | SmallTech    | 2001        | 5844HAJ  | 11/22/2017   |
| 100      | SmallTech    | 3188        | 5521LRO  | 12/31/2017   |
| 200      | RealSolution | 5109        | 8715JUI  | 05/01/2017   |
| 100      | SmallTech    | 3232        | 8715JUI  | 11/01/2017   |
| 200      | RealSolution | 2101        | 4715TEN  | 01/01/2017   |

Comme vous pouvez le constater, les troisième et quatrième rangées montrent deux fournisseurs différents pour le même numéro de pièce.

Voici ma requête actuelle:

WITH

-- various other subqueries above...

    AllData AS
    (
        SELECT VEND_NUM, VEND_NAME, RECEIPT_NUM, RECEIPT_ITEM, RECEIPT_DATE
        FROM tblVend
            INNER JOIN tblReceipt ON VEND_NUM = RECEIPT_VEND_NUM
        WHERE
            VEND_NUM = '100' OR VEND_NUM = '200' AND RECEIPT_DATE >= '01-Jan-2017'
    ),

    SELECT MAX(RECEIPT_DATE) OVER PARTITION BY(RECEIPT_ITEM) AS "Recent Date", RECEIPT_ITEM
    FROM AllData

Mon ensemble de retour ressemble à:

| Recent Date | RECEIPT_ITEM |
|-------------|--------------|
| 11/22/2017  | 5844HAJ      |
| 12/31/2017  | 5521LRO      |
| 11/01/2017  | 8715JUI      |
| 11/01/2017  | 8715JUI      |
| 01/01/2017  | 4715TEN      |

Cependant, cela devrait ressembler à ceci:

| Recent Date | RECEIPT_ITEM |
|-------------|--------------|
| 11/22/2017  | 5844HAJ      |
| 12/31/2017  | 5521LRO      |
| 11/01/2017  | 8715JUI      |
| 01/01/2017  | 4715TEN      |

Quelqu'un peut-il s'il vous plaît donner des conseils quant à ce que je fais mal? Il semble que cela remplace simplement la date la plus récente, sans me donner la rangée que je veux la plus récente.

En fin de compte, j'aimerais que ma table ressemble à ceci. Cependant, je ne sais pas comment utiliser correctement les fonctions MAX () ou MAX () OVER PARTITION BY () pour permettre cela:

| VEND_NUM | VEND_NAME    | RECEIPT_NUM | RECEIPT_ITEM | RECEIPT_DATE |
|----------|--------------|-------------|----------|--------------|
| 100      | SmallTech    | 2001        | 5844HAJ  | 11/22/2017   |
| 100      | SmallTech    | 3188        | 5521LRO  | 12/31/2017   |
| 100      | SmallTech    | 3232        | 8715JUI  | 11/01/2017   |
| 200      | RealSolution | 2101        | 4715TEN  | 01/01/2017   |
1
Jerry M.

Utilisez la fonction de fenêtre ROW_NUMBER() OVER (PARTITION BY receipt_item ORDER BY receipt_date DESC) pour attribuer un numéro de séquence à chaque ligne. La ligne avec le receipt_date le plus récent pour un receipt_item sera numérotée 1.

WITH
-- various other subqueries above...

    AllData AS
    (
        SELECT VEND_NUM, VEND_NAME, RECEIPT_NUM, RECEIPT_ITEM, RECEIPT_DATE,
        ROW_NUMBER() OVER (PARTITION BY RECEIPT_ITEM ORDER BY RECEIPT_DATE DESC ) AS RN
        FROM tblVend
            INNER JOIN tblReceipt ON VEND_NUM = RECEIPT_VEND_NUM
        WHERE
            VEND_NUM IN ( '100','200')  AND RECEIPT_DATE >= '01-Jan-2017'
    )
   SELECT VEND_NUM, VEND_NAME, RECEIPT_NUM, RECEIPT_ITEM, RECEIPT_DATE
   FROM AllData WHERE RN = 1
2
kc2018

Je vois quelques problèmes ici. Premièrement, la syntaxe d'utilisation de la fonction d'agrégation MAX() en tant que fonction analytique (qui est ce que Oracle appelle utilement une fonction de fenêtre) est la suivante:

MAX(receipt_date) OVER ( PARTITION BY receipt_item )

(notez la position des parenthèses). Deuxièmement, à partir de votre jeu de résultats souhaité, vous ne voulez pas réellement de fonction de fenêtre, vous voulez agréger. Une fonction window (ou analytique) renverra toujours une ligne pour chaque ligne de sa partition. ça fonctionne comme ça. Donc, je pense que ce que vous voulez, c'est ceci:

WITH
-- various other subqueries above...
AllData AS
(
    SELECT VEND_NUM, VEND_NAME, RECEIPT_NUM, RECEIPT_ITEM, RECEIPT_DATE
      FROM tblVend
     INNER JOIN tblReceipt ON VEND_NUM = RECEIPT_VEND_NUM
     WHERE ( VEND_NUM = '100' OR VEND_NUM = '200' ) AND RECEIPT_DATE >= DATE'2017-01-01'
)
SELECT VEND_NUM, VEND_NAME, RECEIPT_NUM, RECEIPT_ITEM, MAX(RECEIPT_DATE)
  FROM AllData
 GROUP BY VEND_NUM, VEND_NAME, RECEIPT_NUM, RECEIPT_ITEM;

Maintenant, j’ai apporté quelques petites modifications à ce qui précède, telles que l’encadrement des parenthèses autour des conditions OR (utiliser IN ('100','200') pourrait être encore meilleur) puisque AND a priorité sur OR (votre requête aurait donc obtenu des résultats où VEND_NUM = '100' OR ( VEND_NUM = '200' RECEIPT_DATE >= DATE'2017-01-01' ) ... mais peut-être que voulait?).

1
David Faber

CECI RÉPOND À LA VERSION D'ORIGINE DE LA QUESTION.

Votre clause where devrait probablement ressembler à ceci:

 WHERE VEND_NUM IN ('100', '200') AND RECEIPT_DATE >= DATE '2017-01-01'

Il est fort possible que ce que vous souhaitiez soit simplement:

SELECT DISTINCT RECEIPT_DATE, RECEIPT_ITEM
FROM tblVend INNER JOIN
     tblReceipt
     ON VEND_NUM = RECEIPT_VEND_NUM
WHERE VEND_NUM IN ('100', '200') AND RECEIPT_DATE >= DATE '2017-01-01';

À tout le moins, cela retourne ce que vous voulez retourner.

0
Gordon Linoff