T-SQL: ¿Cuál es la forma más eficiente de recorrer una tabla hasta que se cumpla una condición?

10

En consiguió una tarea de programación en el área de T-SQL.

Tarea:

  1. La gente quiere entrar a un elevador, cada persona tiene un cierto peso.
  2. El orden de las personas que esperan en la fila está determinado por el giro de la columna.
  3. El elevador tiene una capacidad máxima de <= 1000 lbs.
  4. ¡Devuelve el nombre de la última persona que puede entrar al elevador antes de que sea demasiado pesado!
  5. El tipo de retorno debe ser table

ingrese la descripción de la imagen aquí

Pregunta: ¿Cuál es la forma más eficiente de resolver este problema? Si el bucle es correcto, ¿hay margen de mejora?

Utilicé un bucle y # tablas temporales, aquí mi solución:

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 

Aquí el script de creación para la tabla que usé Northwind para probar:

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)
;
Leyendas
fuente

Respuestas:

16

Debe intentar evitar los bucles en general. Normalmente son menos eficientes que las soluciones basadas en conjuntos, así como menos legibles.

Lo siguiente debería ser bastante eficiente.

Más aún si las columnas de nombre y peso se pueden incluir INCLUDE-en el índice para evitar las búsquedas de claves.

Puede escanear el índice único en orden turny calcular el total acumulado de la Weightcolumna, luego usarlo LEADcon los mismos criterios de pedido para ver cuál será el total acumulado en la siguiente fila.

Tan pronto como encuentre la primera fila donde esto excede 1000 o es NULL(lo que indica que no hay una fila siguiente), puede detener el escaneo.

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 de ejecución

ingrese la descripción de la imagen aquí

En la práctica, parece leer unas pocas filas por delante de donde es estrictamente necesario: parece que cada par agregado de spool / flujo de ventana hace que se lean dos filas adicionales.

Para los datos de muestra en la pregunta idealmente, solo necesitaría leer dos filas del escaneo del índice, pero en realidad lee 6, pero este no es un problema de eficiencia significativo y no se degrada a medida que se agregan más filas a la tabla (como en esta demo )

Para aquellos interesados ​​en este tema query_trace_column_values, a continuación se muestra una imagen con las filas de salida de cada operador (como se muestra en el evento extendido), las filas se muestran en row_idorden (comenzando 47por la primera fila leída por el escaneo de índice y terminando 113por el TOP)

Haga clic en la imagen a continuación para agrandarla o, alternativamente, vea la versión animada para que el flujo sea más fácil de seguir .

Pausar la animación en el punto donde el agregado de flujo de la mano derecha ha emitido su primera fila (para gary - turn = 1). Parece evidente que estaba esperando recibir su primera fila con un WindowCount diferente (para Jo - turn = 2). Y el carrete de la ventana no libera la primera fila "Jo" hasta que haya leído la siguiente fila con una diferente turn(para thomas - turn = 3)

Por lo tanto, el spool de ventana y el agregado de flujo hacen que se lea una fila adicional y hay cuatro de estos en el plan, por lo tanto, 4 filas adicionales.

ingrese la descripción de la imagen aquí

A continuación se incluye una explicación de las columnas que se muestran en lo anterior (según la información aquí )

  • NodeName: Index Scan, NodeId: 15, ColumnName: columna de la tabla base de id cubierta por índice
  • NodeName: Index Scan, NodeId: 15, ColumnName: gire la columna de la tabla base cubierta por el índice
  • NodeName: índice de búsqueda en clúster, NodeId: 17, ColumnName: columna de tabla de base de peso recuperada de la búsqueda
  • NodeName: Búsqueda de índice agrupado, NodeId: 17, ColumnName: columna de tabla base de nombre recuperada de la búsqueda
  • NodeName: Segment, NodeId: 13, ColumnName: Segment1010 Devuelve 1 al inicio del nuevo grupo o nulo de lo contrario. Como no Partition Byen la SUMúnica, la primera fila obtiene 1
  • NodeName: Sequence Project, NodeId: 12, ColumnName: RowNumber1009 row_number() dentro del grupo indicado por el indicador Segment1010. Como todas las filas están en el mismo grupo, se trata de enteros ascendentes del 1 al 6. Se utilizaría para filtrar las filas del marco derecho en casos como rows between 5 preceding and 2 following. (o como para LEADmás tarde)
  • NodeName: Segment, NodeId: 11, ColumnName: Segment1011 Devuelve 1 al inicio del nuevo grupo o nulo de lo contrario. Como no Partition Byen la SUMúnica, la primera fila obtiene 1 (igual que el Segmento1010)
  • NodeName: Spool Window, NodeId: 10, ColumnName: WindowCount1012 Atributo que agrupa las filas que pertenecen a un marco de ventana. Este carrete de ventana está utilizando el caso de "vía rápida" UNBOUNDED PRECEDING. Donde emite dos filas por fila de origen. Uno con los valores acumulativos y otro con los valores de detalle. Aunque no hay una diferencia visible en las filas expuestas por query_trace_column_values, supongo que las columnas acumulativas están allí en realidad.
  • NodeName: Stream Aggregate, NodeId: 9, ColumnName: Expr1004 Count(*) agrupado por WindowCount1012 de acuerdo con el plan pero en realidad un recuento continuo
  • NodeName: Stream Aggregate, NodeId: 9, ColumnName: Expr1005 SUM(weight) agrupado por WindowCount1012 de acuerdo con el plan pero en realidad la suma de peso de ejecución (es decir cume_weight)
  • NodeName: Segment, NodeId: 7, ColumnName: Expr1002 CASE WHEN [Expr1004]=(0) THEN NULL ELSE [Expr1005] END - No veo cómo COUNT(*)puede ser 0, por lo que siempre se ejecutará sum ( cume_weight)
  • NodeName: Segment, NodeId: 7, ColumnName: Segment1013 No partition byen la LEADprimera fila obtiene 1. Todos los restantes quedan nulos
  • NodeName: Sequence Project, NodeId: 6, ColumnName: RowNumber1006 row_number() dentro del grupo indicado por el indicador Segment1013. Como todas las filas están en el mismo grupo, se trata de enteros ascendentes del 1 al 4
  • NodeName: Segment, NodeId: 4, ColumnName: BottomRowNumber1008 RowNumber1006 + 1, ya que LEADrequiere la siguiente fila individual
  • NodeName: Segment, NodeId: 4, ColumnName: TopRowNumber1007 RowNumber1006 + 1, ya que LEADrequiere la siguiente fila individual
  • NodeName: Segment, NodeId: 4, ColumnName: Segment1014 No partition byen la LEADprimera fila obtiene 1. Todos los restantes quedan nulos
  • NodeName: Window Spool, NodeId: 3, ColumnName: WindowCount1015 Atributo que agrupa las filas que pertenecen a un marco de ventana utilizando los números de fila anteriores. El marco de la ventana LEADtiene un máximo de 2 filas (la actual y la siguiente)
  • NodeName: Stream Aggregate, NodeId: 2, ColumnName: Expr1003 LAST_VALUE([Expr1002]) para elLEAD(cume_weight)
Martin Smith
fuente
6

Como curiosidad (ya que la pregunta dice T-SQL), también es posible resolver este problema de manera eficiente utilizando SQLCLR.

La idea es leer las filas una a la vez en turnorden hasta que weightexceda 1000 (o nos quedamos sin filas), luego devolver la última namelectura.

El código fuente es:

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;
            }
        }
    }
}

El ensamblado compilado y la función 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;

Obteniendo el resultado:

SELECT dbo.Elevator();
Paul White 9
fuente
1

Ligera variación de la solución 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 es el marco de ventana predeterminado, así que no lo declare.

Se utiliza un predicado para el peso acumulado actual en lugar del siguiente peso acumulado.

No he verificado ningún plan, así que no puedo decir si hay una diferencia en ese sentido.

Lennart
fuente
Ya veo, estoy rodeado de geeks DB :-). Tengo que revisar todas las palabras clave que mencionan para comprender lo que hacen. Solo he echado un vistazo Client statistics --> Total Execution Time, no el Actual execution planque probablemente sea el más interesante aquí. A medida que Client Statisticssu solución es un poco más lenta que la de Martin. Gracias por la información adicional. ¿Qué método se puede usar para medir las diferencias de rendimiento entre diferentes enfoques?
Leyendas
1
Me temo que mi conocimiento del servidor SQL es muy limitado, por lo que no tengo mucha información sobre qué métricas usar. Martin tiene un enlace de violín db <> en su respuesta, tal vez pueda ver los planes allí.
Lennart
1
Tampoco he verificado los planes, pero imagino que esto probablemente calculará el total acumulado en toda la tabla y luego ordenará las filas resultantes que coinciden con el DÓNDE. Dudo que use la restricción de verificación para saber que el total acumulado es estrictamente ascendente y puede detenerse antes. También en SQL Server, excepto donde se usa el agregado de la ventana del modo por lotes, es preferible especificar ROWS en lugar de RANGE, incluso cuando no hay duplicados, ya que el spool de la ventana está en la memoria y no en el disco
Martin Smith
@MartinSmith, interesante. ¿En su solución, LEAD hace posible empujar el predicado next_cume_weight <10000 dentro de T1 y rescatarlo antes del escaneo del índice? Verifiqué el plan para mi consulta e ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROWintroduje un Sequence Project (Compute Scalar)operador. No hace falta decir que no tengo idea de lo que esto significa :-)
Lennart
1
El índice entrega las filas en el orden que necesitan la suma, el plomo y la parte superior. Tan pronto como top reciba su primera fila, puede dejar de solicitar más filas y la ejecución puede detenerse.
Martin Smith
0

Puedes hacer una unión contra sí mismo:

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 ;

Este tipo de cosas no es muy eficiente, ya que provoca una selección por fila. Pero al menos se expresa como una sola declaración.

Si no tiene que hacerlo completamente en SQL, simplemente puede seleccionar todas las filas y recorrerlas, sumando a medida que avanza.

También podría hacer lo mismo en un procedimiento almacenado sin la tabla temporal. Simplemente mantenga la suma y el último nombre de fila en una variable.

ypercubeᵀᴹ
fuente
Lo siento, no sé cómo hacerlo funcionar con un self-join, si pudiera hacer un pequeño ejemplo reproducible, he agregado la definición de tabla a mi pregunta. Mi sql es malo ... Necesito el nombre de la persona más cercana a <= 1000 lbs.
Leyendas
Parece que su actualización funciona bien, deberá manipularla un poco si desea que produzca solo el resultado exacto. Pero como digo, no es súper eficiente
¿De acuerdo? Me pongo nulo para Persona con id 5 ...
Legends
eso es extraño, esperaría que sum () devuelva 0 para una suma en 0 filas
SUM sobre 0 filas no es 0 (desafortunadamente). Necesitas usar COALESCE()o ISNULL()función o una CASEexpresión para que sea 0.
ypercubeᵀᴹ