1
votes

Comment obtenir la ligne précédente et suivante en fonction de la condition

J'essaie d'obtenir la déclaration sur la récupération des lignes précédentes et suivantes d'une ligne sélectionnée.

Declare @OderDetail table
(
    Id int primary key,
    OrderId int,
    ItemId int,
    OrderDate DateTime2,
    Lookup varchar(15)
)

INSERT INTO @OderDetail 
VALUES  
(1, 10, 1, '2018-06-11', 'A'), 
(2, 10, 2, '2018-06-11', 'BE'), --this
(3, 2, 1, '2018-06-04', 'DR'),
(4, 2, 2, '2018-06-04', 'D'),  --this
(5, 3, 2, '2018-06-14', 'DD'), --this
(6, 4, 2, '2018-06-14', 'R');


DECLARE 
    @ItemId int = 2,
    @orderid int = 10

Sortie requise:

L'entrée pour la procédure est order id = 10 et item id = 2 et je dois vérifier que l'article 2 est dans n'importe quel autre ordre, c'est-à-dire uniquement l'article précédent et suivant de l'enregistrement / de la commande correspondant à la date de la commande


2 commentaires

Quelle est votre version de serveur?


Serveur SQL 2017


4 Réponses :


0
votes

Je pense que c'est simple, vous pouvez vérifier avec min (Id) et Max (id) avec une jointure externe gauche ou une application externe

comme

Declare @ItemID int = 2
Select * From @OderDetail A
Outer Apply (
    Select MIN(A2.Id) minID, MAX(A2.Id) maxID From @OderDetail A2
    Where A2.ItemId =@ItemID
) I05
Outer Apply(
    Select * From @OderDetail Where Id=minID-1
    Union All
    Select * From @OderDetail Where Id=maxID+1
    ) I052
Where A.ItemId =@ItemID Order By A.Id

Faites-moi savoir si cela vous aide ou si vous rencontrez un problème avec cela ...

Cordialement,


2 commentaires

merci mais il retourne 4 lignes mais en fait 3 lignes .. je dois trouver l'ID d'ordet = 1 et l'id de l'item = 1


ohhh j'ai manqué le paramètre orderId :) il vous suffit d'ajouter ce paramètre ...



1
votes

C'est ce que vous recherchez? (Mis à jour pour refléter la modification [OrderDate] à la question)

Id  OrderId ItemId  Lookup
2   10      2       BE
4   2       2       D
5   3       2       DD

Requête

With cte As
(
Select ROW_NUMBER() OVER(ORDER BY OrderDate) AS RecN,
* 
From @OderDetail Where ItemId=@ItemId
) 
Select Id, OrderId, ItemId, [Lookup] From cte Where 
RecN Between ((Select Top 1 RecN From cte Where OrderId = @orderid) -1) And
((Select Top 1 RecN From cte Where  OrderId = @orderid) +1) 
Order by id

Résultat:

Declare @OderDetail table
(
    Id int primary key,
    OrderId int,
    ItemId int,
    OrderDate DateTime2,
    Lookup varchar(15)
)

INSERT INTO @OderDetail 
VALUES  
(1, 10, 1, '2018-06-11', 'A'), 
(2, 10, 2, '2018-06-11', 'BE'), --this
(3, 2, 1, '2018-06-04', 'DR'),
(4, 2, 2, '2018-06-04', 'D'),  --this
(5, 3, 2, '2018-06-14', 'DD'), --this
(6, 4, 2, '2018-06-14', 'R');

declare @ItemId  int=2 , @orderid int = 10;


2 commentaires

J'ai mis à jour la question ... veuillez mettre à jour ROW_NUMBER () OVER (ORDER BY OrderDate) AS RecN,


J'ai mis à jour cela selon la modification de la question pour la colonne OrderDate et la variable @Orderid.



1
votes

Mettre à jour pour donner cet ensemble de données: je vois où vous en êtes. Notez que dans CERTAINS cas, il n'y a pas de ligne avant celle donnée - donc cela ne renvoie que 2 et non 3. Ici, j'ai mis à jour la version CTE. Annulez le commentaire de la ligne OTHER pour voir 3 et non 2 car il y en a alors un avant la ligne sélectionnée avec cet Itemid.

Ajout d'une variable pour démontrer comment c'est mieux vous permettant d'obtenir 1 avant et après ou 2 avant / après si vous modifiez ce nombre (c'est-à-dire passez un paramètre) - et si moins de lignes, ou aucune n'est avant ou après, il en obtient autant que possible dans cette contrainte.

Configuration des données pour toutes les versions:

SELECT 
        u.Id,
        u.OrderId,
        u.OrderDate,
        u.ItemId,
        u.Lookup
FROM (
    SELECT 
        a.Id,
        a.OrderId,
        a.OrderDate,
        a.ItemId,
        a.Lookup
    FROM @OderDetail AS a
    WHERE 
         a.ItemId = @ItemId
         AND a.OrderId = @orderid
    UNION 
    SELECT top 1
        b.Id,
        b.OrderId,
        b.OrderDate,
        b.ItemId,
        b.Lookup
    FROM @OderDetail AS b
    WHERE 
         b.ItemId = @ItemId
         AND b.OrderId != @orderid
    ORDER BY b.OrderDate desc, b.OrderId
    UNION 
    SELECT top 1
        b.Id,
        b.OrderId,
        b.OrderDate,
        b.ItemId,
        b.Lookup
    FROM @OderDetail AS b
    WHERE 
         b.ItemId = @ItemId
        AND b.OrderId != @orderid
    ORDER BY b.OrderDate asc, b.OrderId 
) AS u
ORDER BY u.OrderDate asc, u.OrderId 

Version mise à jour CTE:

SELECT TOP  3
    a.Id,
    a.OrderId,
    a.ItemId,
    a.Lookup
FROM @OderDetail AS a
WHERE 
     a.ItemId = @ItemId

Vous voulez probablement la méthode CTE ( Voir un original à la fin de ce strong>) cependant:

Juste pour souligner, cela donne les bons résultats mais ce n'est probablement pas ce que vous recherchez car cela dépend de l'ordre des lignes et de l'ID de l'élément, pas de la ligne réelle avec ces deux valeurs:

DECLARE @rowsBeforeAndAfter INT = 1;
;WITH cte AS (
    SELECT 
        Id,
        OrderId,
        ItemId,
        OrderDate,
        [Lookup],
        ROW_NUMBER() OVER (ORDER BY OrderDate,Id) AS RowNumber
    FROM @OderDetail
    WHERE 
        ItemId = @itemId -- all matches of this
),
myrow AS (
    SELECT TOP 1
        Id,
        OrderId,
        ItemId,
        OrderDate,
        [Lookup],
        RowNumber
    FROM cte
    WHERE 
        ItemId = @itemId 
        AND OrderId = @orderid
)
SELECT 
    cte.Id,
    cte.OrderId,
    cte.ItemId,
    cte.OrderDate,
    cte.[Lookup],
    cte.RowNumber
FROM ctE
INNER JOIN myrow
    ON ABS(cte.RowNumber - myrow.RowNumber) <= @rowsBeforeAndAfter
ORDER BY OrderDate, OrderId;

Pour résoudre ce problème, vous pouvez utiliser un ORDER BY et TOP 1 avec un UNION , un peu moche. (MISE À JOUR avec le tri par date et! = Sur l'id.)

Declare @OderDetail table
(
    Id int primary key,
    OrderId int,
    ItemId int,
    OrderDate DateTime2,
    Lookup varchar(15)
)

INSERT INTO @OderDetail 
VALUES  
(1, 10, 1, '2018-06-11', 'A'), 
(2, 10, 2, '2018-06-11', 'BE'), --this
(3, 2, 1, '2018-06-04', 'DR'),
(4, 2, 2, '2018-06-04', 'D'),  --this
(5, 3, 2, '2018-06-14', 'DD'), --this
(9, 4, 2, '2018-06-14', 'DD'), 
(6, 4, 2, '2018-06-14', 'R'),
--(10, 10, 2, '2018-06-02', 'BE'), -- un-comment to see one before
(23, 4, 2, '2018-06-14', 'R');

DECLARE 
    @ItemId int = 2,
    @orderid int = 2;


5 commentaires

Merci pour la réponse.


Je ne vois aucune date de commande mentionnée nulle part jusqu'à présent, il aurait été bon de l'avoir dans vos données et notes de questions d'origine. Voyons si nous pouvons accueillir ce changement.


J'ai ajouté une mise à jour incluant une option CTE - qui est probablement la meilleure, le formulaire diffère légèrement des autres réponses un peu.


Merci..mais j'ai mis à jour la question..Ur Cte ainsi que la sous-requête ne fonctionne pas pour l'ID de commande 10 et l'article -id -2 pour les données données dans la question mise à jour. (L'ID de commande n'est pas toujours une séquence) toute suggestion


Pour ces numéros, je vois: Id OrderId ItemId OrderDate Lookup RowNumber 2 10 2 2018-06-11 00: 00: 00.0000000 BE 3 5 3 2 2018-06-14 00: 00: 00.0000000 DD 4 6 4 2 2018- 06-14 00: 00: 00.0000000 R 5



1
votes

Une autre approche possible consiste à utiliser les fonctions LAG () et LEAD () , qui renvoient les données d'une ligne précédente et suivante du même ensemble de résultats.

Id  OrderId ItemId  OrderDate           Lookup
2   10      2       11/06/2018 00:00:00 BE
4   2       2       04/06/2018 00:00:00 D
5   3       2       14/06/2018 00:00:00 DD

Résultat:

-- Table
DECLARE @OrderDetail TABLE (
    Id int primary key,
    OrderId int,
    ItemId int,
    OrderDate DateTime2,
    Lookup varchar(15)
)
INSERT INTO @OrderDetail 
VALUES  
   (1, 10, 1, '2018-06-11', 'A'), 
   (2, 10, 2, '2018-06-11', 'BE'), --this
   (3, 2, 1, '2018-06-04', 'DR'),
   (4, 2, 2, '2018-06-04', 'D'),  --this
   (5, 3, 2, '2018-06-14', 'DD'), --this
   (6, 4, 2, '2018-06-14', 'R');

-- Item and order
DECLARE 
    @ItemId int = 2,
    @orderid int = 10

-- Statement    
-- Get previois and next ID for every order, grouped by ItemId, ordered by OrderDate
;WITH cte AS (
   SELECT
      Id,
      LAG(Id, 1) OVER (PARTITION BY ItemId ORDER BY OrderDate) previousId,
      LEAD(Id, 1) OVER (PARTITION BY ItemId ORDER BY OrderDate) nextId,
      ItemId,
      OrderId,
      Lookup
   FROM @OrderDetail   
)
-- Select current, previous and next order
SELECT od.*
FROM cte
CROSS APPLY (SELECT * FROM @OrderDetail WHERE Id = cte.Id) od
WHERE (cte.OrderId = @orderId) AND (cte.ItemId = @ItemId)
UNION ALL
SELECT od.*
FROM cte
CROSS APPLY (SELECT * FROM @OrderDetail WHERE Id = cte.previousId) od
WHERE (cte.OrderId = @orderId) AND (cte.ItemId = @ItemId)
UNION ALL
SELECT od.*
FROM cte
CROSS APPLY (SELECT * FROM @OrderDetail WHERE Id = cte.nextId) od
WHERE (cte.OrderId = @orderId) AND (cte.ItemId = @ItemId)


4 commentaires

Je ne pense pas que cela fonctionne avec des lacunes dans la colonne ID telle qu'elle est écrite


@MarkSchultheiss Je pense que cela fonctionne même avec des lacunes dans la colonne Id.


Avec les données de la question (a été mis à jour) Id OrderId ItemId OrderDate Lookup 2 10 2 2018-06-11 00: 00: 00.0000000 BE 10 10 2 2018-06-02 00: 00: 00.0000000 BE 4 2 2 2018 -06-04 00: 00: 00.0000000 D 4 2 2 2018-06-04 00: 00: 00.0000000 D 23 4 2 2018-06-14 00: 00: 00.0000000 R si vous ajoutez un intervalle ou deux


Merci ... ce code fonctionnera ... mais selon ma pensée, @ level3looper sera la meilleure solution pour la question originale (avant la mise à jour)