选择不含group by的列

时间:2018-06-18 00:07:50

标签: sql sql-server stored-procedures group-by sql-server-2014

我使用的是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)

我希望从ItemNames表格中选择与BatchRecords表中的Id不同的ItemNameSupplier以及{{1} {}},Quantity以及每个Id的最大ItemName项的其他值。到目前为止,我想出了以下SP,当它GROUP BY抛出错误时它肯定还没有工作。

我可以使用子查询,但是如何满足每个唯一ItemName的最大ID的条件?此外,对存储过程质量/明显瓶颈的任何输入都是高度赞赏的,因为它必须有点快。

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

2 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:1)

看起来像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的行。