T-SQL - Quel est le moyen le plus efficace de parcourir une table jusqu'à ce qu'une condition soit remplie

10

En a obtenu une tâche de programmation dans le domaine de T-SQL.

Tâche:

  1. Les gens veulent entrer dans un ascenseur, chaque personne a un certain poids.
  2. L'ordre des personnes faisant la queue est déterminé par le tour de colonne.
  3. L'ascenseur a une capacité maximale de <= 1000 lb.
  4. Retournez le nom de la dernière personne qui peut entrer dans l'ascenseur avant qu'il ne soit trop lourd!
  5. Le type de retour doit être une table

entrez la description de l'image ici

Question: Quelle est la façon la plus efficace de résoudre ce problème? Si le bouclage est correct, y a-t-il place à amélioration?

J'ai utilisé une boucle et des tables # temp, voici ma solution:

set rowcount 0
-- THE SOURCE TABLE "LINE" HAS THE SAME SCHEMA AS #RESULT AND #TEMP
use Northwind
go

declare @sum int
declare @curr int
set @sum = 0
declare @id int

IF OBJECT_ID('tempdb..#temp','u') IS NOT NULL
    DROP TABLE #temp

IF OBJECT_ID('tempdb..#result','u') IS NOT NULL
    DROP TABLE #result

create table #result( 
    id int not null,
    [name] varchar(255) not null,
    weight int not null,
    turn int not null
)

create table #temp( 
    id int not null,
    [name] varchar(255) not null,
    weight int not null,
    turn int not null
)

INSERT into #temp SELECT * FROM line order by turn

 WHILE EXISTS (SELECT 1 FROM #temp)
  BEGIN
   -- Get the top record
   SELECT TOP 1 @curr =  r.weight  FROM  #temp r order by turn  
   SELECT TOP 1 @id =  r.id  FROM  #temp r order by turn

    --print @curr
    print @sum

    IF(@sum + @curr <= 1000)
    BEGIN
    print 'entering........ again'
    --print @curr
      set @sum = @sum + @curr
      --print @sum
      INSERT INTO #result SELECT * FROM  #temp where [id] = @id  --id, [name], turn
      DELETE FROM #temp WHERE id = @id
    END
     ELSE
    BEGIN    
    print 'breaaaking.-----'
      BREAK
    END 
  END

   SELECT TOP 1 [name] FROM #result r order by r.turn desc 

Voici le script de création pour la table que j'ai utilisée Northwind pour les tests:

USE [Northwind]
GO

/****** Object:  Table [dbo].[line]    Script Date: 28.05.2018 21:56:18 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[line](
    [id] [int] NOT NULL,
    [name] [varchar](255) NOT NULL,
    [weight] [int] NOT NULL,
    [turn] [int] NOT NULL,
PRIMARY KEY CLUSTERED 
(
    [id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY],
UNIQUE NONCLUSTERED 
(
    [turn] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

ALTER TABLE [dbo].[line]  WITH CHECK ADD CHECK  (([weight]>(0)))
GO

INSERT INTO [dbo].[line]
    ([id], [name], [weight], [turn])
VALUES
    (5, 'gary', 800, 1),
    (3, 'jo', 350, 2),
    (6, 'thomas', 400, 3),
    (2, 'will', 200, 4),
    (4, 'mark', 175, 5),
    (1, 'james', 100, 6)
;
Légendes
la source

Réponses:

16

Vous devriez essayer d'éviter les boucles en général. Ils sont normalement moins efficaces que les solutions basées sur des ensembles ainsi que moins lisibles.

Ce qui suit devrait être assez efficace.

Encore plus si les colonnes nom et poids pouvaient être INCLUDE-d dans l'index pour éviter les recherches de clés.

Il peut analyser l'index unique dans l'ordre turnet calculer le total cumulé de la Weightcolonne - puis l'utiliser LEADavec les mêmes critères de classement pour voir quel sera le total cumulé sur la ligne suivante.

Dès qu'il trouve la première ligne où cela dépasse 1000 ou est NULL(indiquant qu'il n'y a pas de ligne suivante), il peut arrêter l'analyse.

WITH T1
     AS (SELECT *,
                SUM(Weight) OVER (ORDER BY turn ROWS UNBOUNDED PRECEDING) AS cume_weight
         FROM   [dbo].[line]),
     T2
     AS (SELECT LEAD(cume_weight) OVER (ORDER BY turn) AS next_cume_weight,
                *
         FROM   T1)
SELECT TOP 1 name
FROM   T2
WHERE  next_cume_weight > 1000
        OR next_cume_weight IS NULL
ORDER  BY turn 

Plan d'exécution

entrez la description de l'image ici

En pratique, il semble lire quelques lignes avant l'endroit strictement nécessaire - il semble que chaque paire d'agrégats de spoule / flux de fenêtre entraîne la lecture de deux lignes supplémentaires.

Pour les données d'exemple dans la question, idéalement, il ne faudrait lire que deux lignes de l'analyse d'index, mais en réalité, il lit 6, mais ce n'est pas un problème d'efficacité significatif et il ne se dégrade pas lorsque plus de lignes sont ajoutées au tableau (comme dans cette démo )

Pour ceux qui sont intéressés par ce problème, une image avec les lignes générées par chaque opérateur (comme illustré par l' query_trace_column_valuesévénement étendu) est ci-dessous, les lignes sont générées dans l' row_idordre (en commençant à 47la première ligne lue par l'analyse d'index et en terminant à 113la TOP)

Cliquez sur l'image ci-dessous pour l'agrandir ou voir la version animée pour rendre le flux plus facile à suivre .

Pause de l'animation au point où l'agrégat de flux de droite a émis sa première ligne (pour gary - turn = 1). Il semble évident qu'il attendait de recevoir sa première ligne avec un WindowCount différent (pour Jo - turn = 2). Et la bobine de fenêtre ne libère pas la première ligne "Jo" jusqu'à ce qu'elle ait lu la ligne suivante avec un autre turn(pour thomas - turn = 3)

Ainsi, le spouleur de fenêtre et l'agrégat de flux entraînent tous deux la lecture d'une ligne supplémentaire et il y en a quatre dans le plan - d'où 4 lignes supplémentaires.

entrez la description de l'image ici

Une explication des colonnes montrées ci-dessus suit (basée sur les informations ici )

  • NodeName: Index Scan, NodeId: 15, ColumnName: colonne de la table de base id couverte par l'index
  • NodeName: Index Scan, NodeId: 15, ColumnName: tourner la colonne de la table de base couverte par l'index
  • NodeName: recherche d'index cluster, NodeId: 17, ColumnName: colonne de la table de base de poids récupérée à partir de la recherche
  • NodeName: recherche d'index en cluster, NodeId: 17, ColumnName: colonne de la table de base du nom récupérée à partir de la recherche
  • NodeName: Segment, NodeId: 13, ColumnName: Segment1010 Renvoie 1 au début du nouveau groupe ou null sinon. Comme non Partition Bydans le SUMseul la première rangée obtient 1
  • NodeName: Sequence Project, NodeId: 12, ColumnName: RowNumber1009 row_number() dans le groupe indiqué par l'indicateur Segment1010. Comme toutes les lignes sont dans le même groupe, ce sont des entiers croissants de 1 à 6. Seraient utilisés pour filtrer les lignes de trame de droite dans des cas comme rows between 5 preceding and 2 following. (ou comme pour LEADplus tard)
  • NodeName: Segment, NodeId: 11, ColumnName: Segment1011 Renvoie 1 au début du nouveau groupe ou null sinon. Comme non Partition Bydans le SUMseul la première ligne obtient 1 (identique à Segment1010)
  • NodeName: Window Spool, NodeId: 10, ColumnName: WindowCount1012 Attribut qui regroupe les lignes appartenant à un cadre de fenêtre. Cette bobine de fenêtre utilise le cas "fast track" pour UNBOUNDED PRECEDING. Où il émet deux lignes par ligne source. Un avec les valeurs cumulées et un avec les valeurs détaillées. Bien qu'il n'y ait aucune différence visible dans les lignes exposées par query_trace_column_valuesje suppose que les colonnes cumulatives sont là en réalité.
  • NodeName: Stream Aggregate, NodeId: 9, ColumnName: Expr1004 Count(*) regroupés par WindowCount1012 selon le plan mais en fait un nombre en cours d'exécution
  • NodeName: Stream Aggregate, NodeId: 9, ColumnName: Expr1005 SUM(weight) regroupés par WindowCount1012 selon le plan mais en fait la somme cumulée du poids (c.-à-d. cume_weight)
  • NodeName: Segment, NodeId: 7, ColumnName: Expr1002 CASE WHEN [Expr1004]=(0) THEN NULL ELSE [Expr1005] END - Ne voyez pas comment COUNT(*)peut être 0 donc sera toujours en cours d'exécution sum ( cume_weight)
  • NodeName: Segment, NodeId: 7, ColumnName: Segment1013 Non partition bysur la LEADpremière ligne donc obtient 1. Tous les autres sont nuls
  • NodeName: Sequence Project, NodeId: 6, ColumnName: RowNumber1006 row_number() dans le groupe indiqué par l'indicateur Segment1013. Comme toutes les lignes sont dans le même groupe, il s'agit d'entiers croissants de 1 à 4
  • NodeName: Segment, NodeId: 4, ColumnName: BottomRowNumber1008 RowNumber1006 + 1 car le LEADrequiert la seule ligne suivante
  • NodeName: Segment, NodeId: 4, ColumnName: TopRowNumber1007 RowNumber1006 + 1 car le LEADrequiert la seule ligne suivante
  • NodeName: Segment, NodeId: 4, ColumnName: Segment1014 Non partition bysur la LEADpremière ligne donc obtient 1. Tous les autres sont nuls
  • NodeName: Window Spool, NodeId: 3, ColumnName: WindowCount1015 Attribut qui regroupe les lignes appartenant à un cadre de fenêtre en utilisant les numéros de ligne précédents. Le cadre de la fenêtre pour LEADa au maximum 2 lignes (la suivante et la suivante)
  • NodeName: Stream Aggregate, NodeId: 2, ColumnName: Expr1003 LAST_VALUE([Expr1002]) pour leLEAD(cume_weight)
Martin Smith
la source
6

Tout comme une curiosité (puisque la question indique T-SQL), il est également possible de résoudre ce problème efficacement en utilisant SQLCLR.

L'idée est de lire les lignes une par une dans l' turnordre jusqu'à ce que le weightdépasse 1000 (ou nous manquons de lignes), puis de retourner la dernière namelecture.

Le code source est:

using Microsoft.SqlServer.Server;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;

public partial class UserDefinedFunctions
{
    [SqlFunction(DataAccess = DataAccessKind.Read,
        SystemDataAccess = SystemDataAccessKind.None,
        IsDeterministic = true, IsPrecise = true)]
    [return: SqlFacet(IsFixedLength = false, IsNullable = true, MaxSize = 255)]
    public static SqlString Elevator()
    {
        const string query =
            @"SELECT L.[name], L.[weight]
            FROM dbo.line AS L
            ORDER BY L.turn;";

        using (var con = new SqlConnection("context connection = true"))
        {
            con.Open();
            using (var cmd = new SqlCommand(query, con))
            {
                var rdr = cmd.ExecuteReader(CommandBehavior.SingleResult);
                var name = SqlString.Null;
                var total = 0;

                while (rdr.Read() && (total += rdr.GetInt32(1)) <= 1000)
                {
                    name = rdr.GetSqlString(0);
                }
                return name;
            }
        }
    }
}

L'assembly compilé et la fonction T-SQL:

CREATE ASSEMBLY Elevator AUTHORIZATION [dbo]
FROM 0x4D5A90000300000004000000FFFF0000B800000000000000400000000000000000000000000000000000000000000000000000000000000000000000800000000E1FBA0E00B409CD21B8014CCD21546869732070726F6772616D2063616E6E6F742062652072756E20696E20444F53206D6F64652E0D0D0A2400000000000000504500004C010300606C0E5B0000000000000000E00022200B013000000C000000060000000000004A2A0000002000000040000000000010002000000002000004000000000000000600000000000000008000000002000000000000030060850000100000100000000010000010000000000000100000000000000000000000F82900004F00000000400000A802000000000000000000000000000000000000006000000C000000C02800001C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000080000000000000000000000082000004800000000000000000000002E74657874000000500A000000200000000C000000020000000000000000000000000000200000602E72737263000000A80200000040000000040000000E0000000000000000000000000000400000402E72656C6F6300000C00000000600000000200000012000000000000000000000000000040000042000000000000000000000000000000002C2A0000000000004800000002000500F0200000D007000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001B30030070000000010000117201000070730600000A0A066F0700000A723500007006730800000A0B07176F0900000A0C7E0A00000A0D1613042B0808166F0B00000A0D086F0C00000A2C14110408176F0D00000A5825130420E803000031DC091305DE14072C06076F0E00000ADC062C06066F0E00000ADC11052A011C000002001D003C59000A0000000002000B005863000A000000001E02280F00000A2A42534A4201000100000000000C00000076342E302E33303331390000000005006C000000AC010000237E0000180200002002000023537472696E677300000000380400008801000023555300C0050000100000002347554944000000D00500000002000023426C6F620000000000000002000001471502000900000000FA01330016000001000000110000000200000002000000010000000F000000050000000200000001000000020000000000E70001000000000006008A00A9010600BC00A9010600610096010F00C901000006000202F9000A0075005C010A003E005C010A0038005C010A00AA005C010A00DD00D8010A00120109020A002D0009020A00400109020A00050120010A00770113000A003301200106004D00F900000000000A00000000000100010001001000ED0100001500010001005020000000009600870150000100E820000000008618900106000200000000000000090090010100110090010600190090010A00310090010600490090010600590090011E0071000001060061009001230061004E012A005100F40031006900DA003500810028003B00810001003F0089005900060029009001060020002300860024002B00CE012E000B0055002E0013005E002E001B007D00100044000480000000000000000000000000000000008701000004000000000000000000000047001F000000000004000000000000000000000047001300000000000000000000476574496E743332003C4D6F64756C653E0053797374656D2E44617461006D73636F726C696200526561640053716C436F6D6D616E640053797374656D446174614163636573734B696E640049446973706F7361626C6500446973706F73650044656275676761626C654174747269627574650053716C46756E6374696F6E41747472696275746500436F6D70696C6174696F6E52656C61786174696F6E734174747269627574650053716C46616365744174747269627574650052756E74696D65436F6D7061746962696C6974794174747269627574650047657453716C537472696E6700456C657661746F722E646C6C004E756C6C0053797374656D004F70656E004462436F6E6E656374696F6E0053716C436F6E6E656374696F6E0053797374656D2E446174612E436F6D6D6F6E004462446174615265616465720053716C446174615265616465720045786563757465526561646572004D6963726F736F66742E53716C5365727665722E53657276657200436F6D6D616E644265686176696F7200456C657661746F72002E63746F720053797374656D2E446961676E6F73746963730053797374656D2E52756E74696D652E436F6D70696C6572536572766963657300446562756767696E674D6F6465730053797374656D2E446174612E53716C54797065730055736572446566696E656446756E6374696F6E73004F626A6563740053797374656D2E446174612E53716C436C69656E740000003363006F006E007400650078007400200063006F006E006E0065006300740069006F006E0020003D00200074007200750065000081510D000A00200020002000200020002000200020002000200020002000530045004C004500430054000D000A0020002000200020002000200020002000200020002000200020002000200020004C002E005B006E0061006D0065005D002C000D000A0020002000200020002000200020002000200020002000200020002000200020004C002E005B007700650069006700680074005D000D000A00200020002000200020002000200020002000200020002000460052004F004D002000640062006F002E006C0069006E00650020004100530020004C000D000A002000200020002000200020002000200020002000200020004F0052004400450052002000420059000D000A0020002000200020002000200020002000200020002000200020002000200020004C002E007400750072006E003B000D000A00200020002000200020002000200020002000200020002000006C68AC85076C6945BCECB2CE73B9787F000420010108032000010520010111110D0706122D123112351129081129042001010E062002010E122D0620011235113D0306112905200111290803200002042001080802060E08B77A5C561934E08904000011290801000800000000001E01000100540216577261704E6F6E457863657074696F6E5468726F7773010801000200000000008146010004005455794D6963726F736F66742E53716C5365727665722E5365727665722E446174614163636573734B696E642C2053797374656D2E446174612C2056657273696F6E3D342E302E302E302C2043756C747572653D6E65757472616C2C205075626C69634B6579546F6B656E3D623737613563353631393334653038390A446174614163636573730100000054557F4D6963726F736F66742E53716C5365727665722E5365727665722E53797374656D446174614163636573734B696E642C2053797374656D2E446174612C2056657273696F6E3D342E302E302E302C2043756C747572653D6E65757472616C2C205075626C69634B6579546F6B656E3D623737613563353631393334653038391053797374656D446174614163636573730000000054020F497344657465726D696E69737469630154020949735072656369736501310100030054020D497346697865644C656E6774680054020A49734E756C6C61626C65015408074D617853697A65FF00000000000000606C0E5B00000000020000001C010000DC280000DC0A000052534453F8D0A90B6069F74DB5FBFABECC9D213901000000633A5C75736572735C7061756C775C736F757263655C7265706F735C4461746162617365325C4461746162617365325C6F626A5C52656C656173655C456C657661746F722E7064620000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000202A000000000000000000003A2A00000020000000000000000000000000000000000000000000002C2A0000000000000000000000005F436F72446C6C4D61696E006D73636F7265652E646C6C0000000000FF2500200010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001001000000018000080000000000000000000000000000001000100000030000080000000000000000000000000000001000000000048000000584000004C02000000000000000000004C0234000000560053005F00560045005200530049004F004E005F0049004E0046004F0000000000BD04EFFE00000100000000000000000000000000000000003F000000000000000400000002000000000000000000000000000000440000000100560061007200460069006C00650049006E0066006F00000000002400040000005400720061006E0073006C006100740069006F006E00000000000000B004AC010000010053007400720069006E006700460069006C00650049006E0066006F0000008801000001003000300030003000300034006200300000002C0002000100460069006C0065004400650073006300720069007000740069006F006E000000000020000000300008000100460069006C006500560065007200730069006F006E000000000030002E0030002E0030002E00300000003A000D00010049006E007400650072006E0061006C004E0061006D006500000045006C0065007600610074006F0072002E0064006C006C00000000002800020001004C006500670061006C0043006F00700079007200690067006800740000002000000042000D0001004F0072006900670069006E0061006C00460069006C0065006E0061006D006500000045006C0065007600610074006F0072002E0064006C006C0000000000340008000100500072006F006400750063007400560065007200730069006F006E00000030002E0030002E0030002E003000000038000800010041007300730065006D0062006C0079002000560065007200730069006F006E00000030002E0030002E0030002E0030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000C0000004C3A00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
WITH PERMISSION_SET = SAFE;
GO
CREATE FUNCTION dbo.Elevator ()
RETURNS nvarchar(255)
AS EXTERNAL NAME Elevator.UserDefinedFunctions.Elevator;

Obtenir le résultat:

SELECT dbo.Elevator();
Paul White 9
la source
1

Légère variation par rapport à la solution de Martin Smith

SELECT top 1 name
FROM (
    SELECT id, name, weight, turn
         , SUM(weight) OVER (ORDER BY turn) AS cumulative_weight
    FROM line                               
) as T
WHERE cumulative_weight <= 1000
ORDER BY turn DESC 

RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW est le cadre de fenêtre par défaut, donc je ne l'ai pas déclaré.

Un prédicat pour le poids cumulé actuel est utilisé à la place du poids cumulé suivant.

Je n'ai vérifié aucun plan, donc je ne peux pas dire s'il y a une différence à cet égard.

Lennart
la source
Je vois, je suis entouré de geeks DB :-). Je dois vérifier tous les mots-clés que vous mentionnez pour comprendre ce qu'ils font. J'ai seulement jeté un coup d'œil Client statistics --> Total Execution Time, pas celui Actual execution planqui est probablement le plus intéressant ici. A partir de Client Statisticsvotre solution est un petit peu plus lent que Martin. Merci pour l'information supplémentaire. Quelle méthode peut-on utiliser pour mesurer les différences de performances entre différentes approches?
Legends
1
Je crains que ma connaissance de SQL-server soit très limitée, je n'ai donc pas beaucoup d'informations sur les mesures à utiliser. Martin a un lien de violon db <> dans sa réponse, vous pouvez peut-être regarder les plans là-bas.
Lennart
1
Je n'ai pas non plus vérifié les plans, mais j'imagine que cela calculera probablement le total cumulé sur toute la table, puis triera les lignes résultantes correspondant à WHERE. Je doute qu'il utilisera la contrainte de vérification pour savoir que le total cumulé est strictement ascendant et peut s'arrêter tôt. Également dans SQL Server, sauf lorsque l'agrégat de fenêtre en mode batch est utilisé en spécifiant ROWS plutôt que RANGE est préférable même lorsqu'il n'y a pas de doublons car le spool de fenêtre est en mémoire et non sur le disque
Martin Smith
@MartinSmith, intéressant. Dans votre solution, LEAD permet de pousser le prédicat next_cume_weight <10000 à l'intérieur de T1 et de renflouer tôt le scan d'index? J'ai vérifié le plan de ma requête et ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROWprésente un Sequence Project (Compute Scalar)opérateur,. Inutile de dire que je n'ai aucune idée de ce que cela signifie :-)
Lennart
1
L'index fournit les lignes dans l'ordre requis par la somme, le plomb et le haut. Dès que top reçoit sa première ligne, il peut cesser de demander d'autres lignes et l'exécution peut s'arrêter.
Martin Smith
0

Vous pouvez faire une jointure contre elle-même:

select 
    a.id, a.turn, a.game, 
    coalesce(sum(b.weight), 0) as cumulative_weight
from
    table a
left join 
    table b
on
    a.turn > b.turn
group by
    a.id, a.turn, a.game ;

Ce genre de chose n'est pas très efficace car il provoque une sélection par ligne. Mais au moins, il est exprimé en une seule déclaration.

Si vous n'avez pas à le faire entièrement en SQL, vous pouvez simplement sélectionner toutes les lignes et les parcourir, en les additionnant au fur et à mesure.

Vous pouvez également faire de même dans une procédure stockée sans la table temporaire. Maintenez simplement la somme et le nom de la dernière ligne dans une variable.

ypercubeᵀᴹ
la source
Désolé, je ne sais pas comment le faire fonctionner avec un self-join, si vous pouvez faire un petit exemple reproductible, j'ai ajouté la définition du tableau à ma question. Mon sql est mauvais .... J'ai besoin du nom de la personne la plus proche de <= 1000 lbs.
Legends
semble que votre mise à jour fonctionne bien, vous devrez y jouer un peu si vous voulez qu'elle produise juste la sortie exacte. Mais comme je le dis, ce n'est pas super efficace
D'accord? Je suis nul pour la personne avec l'identifiant 5 ...
Legends
c'est étrange, je m'attendrais à ce que sum () renvoie 0 pour une somme sur 0 lignes
SUM sur 0 lignes n'est pas 0 (malheureusement). Vous devez utiliser COALESCE()ou une ISNULL()fonction ou une CASEexpression pour le mettre à 0.
ypercubeᵀᴹ