Select 列合计没有分组依据

Select columns without group by in aggregate

我正在使用 SQL Server 2014。我的结构如下:

Id BIGINT,
ItemName NVARCHAR(4000),
RecordDate DATETIME2,
Supplier NVARCHAR(450),
Quantity DECIMAL(18, 2),
ItemUnit NVARCHAR(2000),
EntityUnit NVARCHAR(2000),
ItemSize DECIMAL(18, 2),
PackageSize DECIMAL(18, 2),
FamilyCode NVARCHAR(20),
Family NVARCHAR(500),
CategoryCode NVARCHAR(20),
Category NVARCHAR(500),
SubCategoryCode NVARCHAR(20),
SubCategory NVARCHAR(500),
ItemGroupCode NVARCHAR(20),
ItemGroup NVARCHAR(500),
PurchaseValue DECIMAL(18, 2),
UnitPurchaseValue DECIMAL(18, 2),
PackagePurchaseValue DECIMAL(18, 2),
FacilityCode NVARCHAR(450),
CurrencyCode NVARCHAR(5)

我想 select 与 BatchRecords ItemNames 不同 table 与具有相同 [=16] 的项目中的最大值 Id 配对=] 以及 SupplierQuantity 和项目的其他值,每个 ItemName 的最大值为 Id。到目前为止,我想出了以下 SP,但肯定还行不通,因为 GROUP BY 会抛出错误。

我或许可以使用子查询,但是我如何满足每个唯一 ItemName 的最大 ID 条件?此外,对存储过程 quality/obvious 瓶颈的任何输入都非常感谢,因为它必须有点快。

CREATE PROCEDURE dbo.GetRecordsPageFlat 
     (@BatchIds dbo.GenericIntArray READONLY,
      @FileRequestId INT,
      @PageSize INT,
      @PageCount INT,
      @LastId BIGINT,
      @NameMaskValue NVARCHAR(128) = NULL,
      @NameMaskType INT = NULL,
      @FamilyCodeMaskValue NVARCHAR(128),
      @CategoryCodeMaskValue NVARCHAR(128),
      @SubCategoryCodeMaskValue NVARCHAR(128)
     )
AS
    SET NOCOUNT ON;

    DECLARE @Temp dbo.RecordImportStructure
    DECLARE @ErrorCode INT
    DECLARE @Step NVARCHAR(200)
    DECLARE @Rows INT

    --OUTPUT @@ROWCOUNT
    --OUTPUT INSERTED.Id
    INSERT INTO @Temp (
        Id,
        ItemName,
        Supplier,
        Quantity,
        ItemUnit,
        EntityUnit,
        ItemSize,
        PackageSize,
        PurchaseValue,
        UnitPurchaseValue,
        PackagePurchaseValue,
        CurrencyCode
    )
    SELECT
        BR.Id,
        BR.ItemName,
        BR.Supplier,
        BR.Quantity,
        BR.ItemUnit,
        BR.EntityUnit,
        BR.ItemSize,
        BR.PackageSize,
        BR.ItemGroup,
        BR.UnitPurchaseValue,
        BR.PackagePurchaseValue,
        C.IsoCode
    FROM   
        dbo.BatchRecords BR
    LEFT OUTER JOIN 
        dbo.FacilityInstances F ON F.Id = BR.FacilityInstanceId
    LEFT OUTER JOIN 
        dbo.Currencies C ON C.Id = BR.CurrencyId
        --OPTION(RECOMPILE)
    WHERE 
        BR.DataBatchId IN (SELECT * FROM @BatchIds)
        AND BR.Id > @LastId
        AND (@FamilyCodeMaskValue IS NULL OR BR.FamilyCode = @FamilyCodeMaskValue)
        AND (@CategoryCodeMaskValue IS NULL OR BR.CategoryCode = @CategoryCodeMaskValue)
        AND (@SubCategoryCodeMaskValue IS NULL OR BR.SubCategoryCode = @SubCategoryCodeMaskValue)
        AND (@NameMaskType IS NULL AND @NameMaskValue IS NULL 
             OR ((@NameMaskType = 1 AND BR.ItemName LIKE @NameMaskValue + '%')
             OR (@NameMaskType = 2 AND BR.ItemName LIKE '%' + @NameMaskValue)
             OR (@NameMaskType = 3 AND BR.ItemName LIKE '%' + @NameMaskValue + '%')
            ))
    GROUP BY 
        BR.ItemName
    ORDER BY 
        BR.Id
        OFFSET @PageCount * @PageSize ROWS
        FETCH NEXT @PageSize ROWS ONLY;

    UPDATE dbo.BatchActionRequests
    SET PageNumber = @PageCount+1,
    LatestItemId = (SELECT MAX(Id) FROM @Temp)
    WHERE Id = @FileRequestId
;WITH CTC 
AS
(
    SELECT MAX(BR.ID) AS Id, BR.ItemName 
    FROM dbo.BatchRecords BR
        LEFT OUTER JOIN dbo.FacilityInstances F ON F.Id = BR.FacilityInstanceId
        WHERE BR.DataBatchId IN (SELECT * FROM @BatchIds)
        AND BR.Id > @LastId
        AND (@FamilyCodeMaskValue IS NULL OR BR.FamilyCode = @FamilyCodeMaskValue)
        AND (@CategoryCodeMaskValue IS NULL OR BR.CategoryCode = @CategoryCodeMaskValue)
        AND (@SubCategoryCodeMaskValue IS NULL OR BR.SubCategoryCode = @SubCategoryCodeMaskValue)
        AND (@NameMaskType IS NULL AND @NameMaskValue IS NULL 
             OR ((@NameMaskType = 1 AND BR.ItemName LIKE @NameMaskValue + '%')
             OR (@NameMaskType = 2 AND BR.ItemName LIKE '%' + @NameMaskValue)
             OR (@NameMaskType = 3 AND BR.ItemName LIKE '%' + @NameMaskValue + '%')
            ))
        GROUP BY 
        BR.ItemName
)
INSERT INTO @Temp (
        Id,
        ItemName,
        Supplier,
        Quantity,
        ItemUnit,
        EntityUnit,
        ItemSize,
        PackageSize,
        PurchaseValue,
        UnitPurchaseValue,
        PackagePurchaseValue,
        CurrencyCode
    )
SELECT  BR.Id,
        BR.ItemName,
        BR.Supplier,
        BR.Quantity,
        BR.ItemUnit,
        BR.EntityUnit,
        BR.ItemSize,
        BR.PackageSize,
        BR.ItemGroup,
        BR.UnitPurchaseValue,
        BR.PackagePurchaseValue,
        C.IsoCode
    FROM CTC t 
    JOIN dbo.BatchRecords BR ON t.Id = BR.Id
    LEFT OUTER JOIN dbo.Currencies C ON C.Id = BR.CurrencyId
    ORDER BY BR.Id
    OFFSET @PageCount * @PageSize ROWS
    FETCH NEXT @PageSize ROWS ONLY;

看起来像 top-n-per-group 问题。

有两种常见的方法:使用 ROW_NUMBERCROSS APPLY。这是 ROW_NUMBER 变体。有关详细信息,请参阅 Get top 1 row of each group

WITH
CTE
AS
(
    SELECT
        BR.Id,
        BR.ItemName,
        BR.Supplier,
        BR.Quantity,
        BR.ItemUnit,
        BR.EntityUnit,
        BR.ItemSize,
        BR.PackageSize,
        -- BR.ItemGroup,???
        BR.UnitPurchaseValue,
        BR.PackagePurchaseValue,
        C.IsoCode AS CurrencyCode,
        ROW_NUMBER() OVER (PARTITION BY BR.ItemName ORDER BY BR.Id DESC) AS rn
    FROM   
        dbo.BatchRecords BR
        LEFT OUTER JOIN dbo.FacilityInstances F ON F.Id = BR.FacilityInstanceId
        LEFT OUTER JOIN dbo.Currencies C ON C.Id = BR.CurrencyId
    WHERE 
        BR.DataBatchId IN (SELECT * FROM @BatchIds)
        AND BR.Id > @LastId
        AND (@FamilyCodeMaskValue IS NULL OR BR.FamilyCode = @FamilyCodeMaskValue)
        AND (@CategoryCodeMaskValue IS NULL OR BR.CategoryCode = @CategoryCodeMaskValue)
        AND (@SubCategoryCodeMaskValue IS NULL OR BR.SubCategoryCode = @SubCategoryCodeMaskValue)
        AND (@NameMaskType IS NULL AND @NameMaskValue IS NULL 
             OR ((@NameMaskType = 1 AND BR.ItemName LIKE @NameMaskValue + '%')
             OR (@NameMaskType = 2 AND BR.ItemName LIKE '%' + @NameMaskValue)
             OR (@NameMaskType = 3 AND BR.ItemName LIKE '%' + @NameMaskValue + '%')
            ))
)
INSERT INTO @Temp (
    Id,
    ItemName,
    Supplier,
    Quantity,
    ItemUnit,
    EntityUnit,
    ItemSize,
    PackageSize,
    -- PurchaseValue,???
    UnitPurchaseValue,
    PackagePurchaseValue,
    CurrencyCode
)
SELECT
    Id,
    ItemName,
    Supplier,
    Quantity,
    ItemUnit,
    EntityUnit,
    ItemSize,
    PackageSize,
    -- PurchaseValue,???
    UnitPurchaseValue,
    PackagePurchaseValue,
    CurrencyCode
FROM CTE
WHERE rn = 1
ORDER BY 
    Id
    OFFSET @PageCount * @PageSize ROWS
    FETCH NEXT @PageSize ROWS ONLY
OPTION(RECOMPILE);

对于每个 ItemName,查询将选择具有最大 Id 的行。