0

正しいデータを返さないSQLサーバーのクエリに問題があります。見ることができるはずのシンプルで簡潔な解決策があることを前もって認めてうれしいですが、私は物事を考えすぎていたと思います。

ビジネスシナリオでは、特定のアイテムに対して請求された価格を、見積もられた価格と比較しています。これはクエリです:

SELECT  ipl1.Id AS Id,
        CASE s1.FirstName WHEN '' THEN s1.LastName ELSE s1.LastName + ', ' + s1.FirstName END AS SupplierName,
        p1.Date,
        p1.OrderNo,
        p1.InvoiceNo,
        i1.Number AS ItemNumber,
        i1.Name AS ItemName,
        ipl1.Quantity,
        ROUND(ipl1.TaxExclusiveUnitPrice, 2) AS PriceCharged,
        ROUND(ISNULL(p2.Amount, p4.Amount), 2) AS PriceQuoted,
        ROUND(ROUND(ipl1.TaxExclusiveUnitPrice, 2) - ROUND(ISNULL(p2.Amount, p4.Amount), 2), 2) AS Difference,
        ipl1.Quantity*ROUND(ROUND(ipl1.TaxExclusiveUnitPrice, 2) - ROUND(ISNULL(p2.Amount, p4.Amount), 2), 2) AS Overcharge,
        ISNULL(p2.Starts, p4.Starts) AS QuoteDate,
        ISNULL(p2.QuoteNo, p4.QuoteNo) AS QuoteNumber
FROM    pl_ItemPurchaseLines ipl1
        INNER JOIN
        pl_Purchases p1 ON p1.Id=ipl1.Purchase_Id
        INNER JOIN
        pl_Suppliers s1 ON s1.Id=p1.Supplier_Id 
        INNER JOIN
        pl_Items i1 ON i1.Id=ipl1.Item_Id 
        LEFT JOIN
        --- First Priority is the lowest Amount Current Quote or List Price
        (
        SELECT  p1.Id,
                p1.Amount,
                p1.Starts,
                p1.Expires,
                p1.QuoteNo,
                ipl.Id AS ItemPurchaseLine_Id,
                row_number() over (partition by ipl.Id order by p1.Amount ASC, p1.QuoteNo DESC, p1.Starts DESC) as Row
        FROM    pl_ItemPurchaseLines ipl
                INNER JOIN
                pl_Purchases p ON p.Id=ipl.Purchase_Id
                INNER JOIN 
                pl_Prices p1 ON p1.Starts<=p.Date AND 
                                ((p1.Expires>=p.Date AND p1.QuoteNo<>'') OR
                                 (p1.Expires IS NULL AND p1.QuoteNo='')) AND
                                 p1.Item_Id=ipl.Item_Id
        ) AS p2 ON  p2.Row = 1 AND
                    ipl1.Id=p2.ItemPurchaseLine_Id
        LEFT JOIN           
        (
        SELECT  p3.Id,
                p3.Amount,
                p3.Starts,
                p3.Expires,
                p3.QuoteNo,
                ipl2.Id AS ItemPurchaseLine_Id,
                row_number() over (partition by ipl2.Id order by p3.Expires DESC) as Row
        FROM    pl_ItemPurchaseLines ipl2
                INNER JOIN
                pl_Purchases p2 ON p2.Id=ipl2.Purchase_Id
                INNER JOIN 
                pl_Prices p3 ON p3.Starts<=p2.Date AND 
                                p3.QuoteNo<>'' AND
                                p3.Item_Id=ipl2.Item_Id
        ) AS p4 ON  p4.Row = 1 AND
                    ipl1.Id=p4.ItemPurchaseLine_Id

問題の根源はサブクエリにあります。価格には2つのタイプがあります。開始日、有効期限なし、および''の見積もりがあり、後者の定価に取って代わられる定価です。または、開始日、有効期限、および見積もり番号を持つ見積もり価格。

問題は最初のサブクエリにあります。ここで私は最低価格のまだ有効な見積もりまたは定価を探していますが、置き換えられた定価、つまりquoteno =''の価格を除外する方法を理解できず、有効期限はnull(これらは簡単です)および他の場所があります同じIDで開始日が遅い価格表。

トリガーに基づいて有効期限をテーブルに入れることを検討しましたが、2つの後者の価格表が同時に入ってくる可能性に対処する必要があるため、いくつかの問題があります(さらに以前の価格表-私たちは逆方向に取り組んでいます転送だけでなく)。

価格IDが与えられたときに関数を記述して、それが特定の日付で置き換えられるかどうかを判断できますが、それは最適ではないようです。

これらの置き換えられた価格をなくすには、where句に何かをする必要があると感じますが、何度か行って、機能しない冗長なバロック句を作成しました。

サンプルデータは次のとおりです。

-- No there isn't I will put some up as soon as I format it --

テーブルの定義は次のとおりです。

USE [tempdb]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[pl_Suppliers](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [LastName] [nvarchar](100) NOT NULL,
    [FirstName] [nvarchar](100) NOT NULL,
    [ShortName] [nvarchar](100) NOT NULL,
    [Prefix] [nvarchar](100) NOT NULL,
    [UserID] [nvarchar](100) NOT NULL,
    [Password] [nvarchar](1000) NOT NULL,
    [SentToMyob] [bit] NOT NULL,
 CONSTRAINT [PK_Suppliers] 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]
) ON [PRIMARY]

GO

CREATE TABLE [dbo].[pl_ItemPurchaseLines](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [Quantity] [float] NOT NULL,
    [TaxExclusiveUnitPrice] [float] NOT NULL,
    [TaxExclusiveTotal] [float] NOT NULL,
    [TaxInclusiveTotal] [float] NOT NULL,
    [TaxBasisAmount] [float] NOT NULL,
    [TaxCode] [nvarchar](max) NOT NULL,
    [Received] [float] NOT NULL,
    [TaxInclusiveUnitPrice] [float] NOT NULL,
    [Purchase_Id] [int] NOT NULL,
    [Item_Id] [int] NOT NULL,
 CONSTRAINT [PK_pl_ItemPurchaseLines] PRIMARY KEY NONCLUSTERED 
(
    [Id] 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

CREATE TABLE [dbo].[pl_Purchases](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [OrderNo] [nvarchar](max) NOT NULL,
    [InvoiceNo] [nvarchar](max) NOT NULL,
    [Date] [datetime] NOT NULL,
    [ShipToAddressLine1] [nvarchar](max) NOT NULL,
    [ShipToAddressLine2] [nvarchar](max) NOT NULL,
    [ShipToAddressLine3] [nvarchar](max) NOT NULL,
    [ShipToAddressLine4] [nvarchar](max) NOT NULL,
    [Comment] [nvarchar](max) NOT NULL,
    [TotalLines] [float] NOT NULL,
    [TotalTax] [float] NOT NULL,
    [Supplier_Id] [int] NOT NULL,
    [SentToMyob] [bit] NOT NULL,
    [Job_ID] [int] NULL,
 CONSTRAINT [PK_Purchases] 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]
) ON [PRIMARY]
GO

CREATE TABLE [dbo].[pl_Items](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [Number] [nvarchar](100) NOT NULL,
    [Name] [nvarchar](255) NOT NULL,
    [Supplier_Id] [int] NOT NULL,
    [SentToMyob] [bit] NOT NULL,
 CONSTRAINT [PK_Items] PRIMARY KEY NONCLUSTERED 
(
    [Id] 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

CREATE TABLE [dbo].[pl_Prices](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [Amount] [float] NOT NULL,
    [Starts] [datetime] NOT NULL,
    [Expires] [datetime] NULL,
    [QuoteNo] [nvarchar](100) NOT NULL,
    [Item_Id] [int] NOT NULL,
 CONSTRAINT [PK_Prices] 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]
) ON [PRIMARY]

GO

ALTER TABLE [dbo].[pl_Prices]  WITH CHECK ADD  CONSTRAINT [FK_ItemPrice] FOREIGN KEY([Item_Id])
REFERENCES [dbo].[pl_Items] ([Id])
ON DELETE CASCADE
GO

ALTER TABLE [dbo].[pl_Prices] CHECK CONSTRAINT [FK_ItemPrice]
GO
ALTER TABLE [dbo].[pl_Items]  WITH CHECK ADD  CONSTRAINT [FK_SupplierItem] FOREIGN KEY([Supplier_Id])
REFERENCES [dbo].[pl_Suppliers] ([Id])
GO

ALTER TABLE [dbo].[pl_Items] CHECK CONSTRAINT [FK_SupplierItem]
GO

ALTER TABLE [dbo].[pl_Items] ADD  CONSTRAINT [DF_pl_Items_SentToMyob]  DEFAULT ((0)) FOR [SentToMyob]
GO

ALTER TABLE [dbo].[pl_Purchases]  WITH CHECK ADD  CONSTRAINT [FK_PurchasesSuppliers] FOREIGN KEY([Supplier_Id])
REFERENCES [dbo].[pl_Suppliers] ([Id])
GO

ALTER TABLE [dbo].[pl_Purchases] CHECK CONSTRAINT [FK_PurchasesSuppliers]
GO

ALTER TABLE [dbo].[pl_Purchases] ADD  CONSTRAINT [DF_pl_Purchases_SentToMyob]  DEFAULT ((0)) FOR [SentToMyob]
GO

ALTER TABLE [dbo].[pl_ItemPurchaseLines]  WITH CHECK ADD  CONSTRAINT [FK_ItemPurchaseLineItem] FOREIGN KEY([Item_Id])
REFERENCES [dbo].[pl_Items] ([Id])
GO

ALTER TABLE [dbo].[pl_ItemPurchaseLines] CHECK CONSTRAINT [FK_ItemPurchaseLineItem]
GO

ALTER TABLE [dbo].[pl_ItemPurchaseLines]  WITH CHECK ADD  CONSTRAINT [FK_Purchase_ItemPurchaseLines] FOREIGN KEY([Purchase_Id])
REFERENCES [dbo].[pl_Purchases] ([Id])
ON DELETE CASCADE
GO

ALTER TABLE [dbo].[pl_ItemPurchaseLines] CHECK CONSTRAINT [FK_Purchase_ItemPurchaseLines]
GO

ALTER TABLE [dbo].[pl_Suppliers] ADD  CONSTRAINT [DF_pl_Suppliers_SentToMyob]  DEFAULT ((0)) FOR [SentToMyob]
GO
4

2 に答える 2

1

ソリューションの副選択にバグがありました:p1.Item_Id = p1.Item_Idこれは、「副選択」テーブル「p1」のみを参照し、「外部選択」テーブル「p」は参照しません。

副選択を使用するソリューションは、大規模なデータセットに対してスケーラブルではありません。

このようなものは、適切なインデックス付けを前提としてスケーラブルになります。

SELECT p.[Id]
  ,p.[Amount]
  ,p.[Starts] 
  ,ISNULL(p.Expires, DATEADD(DAY,-1,MIN(p1.Starts))
  ,p.[QuoteNo]
  ,p.[Item_Id]
FROM    pl_Prices p

LEFT JOIN pl_Prices p1 ON
     p1.Item_Id=p.Item_Id AND    
     p1.Starts>p.Starts
GROUP BY 
      p.[Id]
      ,p.[Amount]
      ,p.[Starts] 
      ,p.[QuoteNo]
      ,p.[Item_Id]
于 2012-12-08T14:58:13.147 に答える
0

私はそれを理解しました-それは私が期待したように単純でした。

このビューを作成し、最初のサブクエリで参照しました。

ALTER VIEW [dbo].[pl_PricesWithExpiry]
AS
SELECT [Id]
      ,[Amount]
      ,[Starts] 
      ,ISNULL(p.Expires, (SELECT DATEADD(DAY,-1,MIN(p1.Starts))
                          FROM pl_Prices p1
                          WHERE p1.Item_Id=p.Item_Id AND
                          p1.Starts>p.Starts)) AS Expires
      ,[QuoteNo]
      ,[Item_Id]
FROM    pl_Prices p
于 2012-12-07T05:14:43.123 に答える