【问题标题】:Sql Procedure - Select data depending on state from another tableSql 过程 - 根据状态从另一个表中选择数据
【发布时间】:2016-02-19 14:55:32
【问题描述】:

我正在尝试为我正在开发的程序创建搜索功能。我目前的查询检查已选择的记录状态,可以是以下任何一种:

  • 有偿和无偿
  • 仅付费
  • 仅限未付款
  • 已删除记录

然后它会执行一个选择查询,该查询会提取与这些记录状态匹配的所有记录,这是查询:

    PROCEDURE [dbo].[SearchSQL]
    -- Add the parameters for the stored procedure here
    @logging_ref as varchar(50) = NULL,
    @summit_ac_no as varchar(50) = NULL,
    @contract_no as varchar(50) = NULL,
    @invoice_no as varchar(50) = NULL,
    @company as varchar(50) = NULL,
    @paycert as varchar(50) = NULL,
    @record_type as integer = NULL,
    @qs as varchar(50) = NULL,
    @records as int = NULL,
    @state as int = NULL
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    -- Insert statements for procedure here
    IF @state = 1 
        Begin
            SELECT TOP(@records) 
            -- Reference Columns - Used to identify a Record --
                r.r_id AS 'ref',  
                grossVal AS 'gross', 
                payCert AS 'cert', 
            -- Data Columns for Display --
                logRef as 'Logging Reference', 
                db_recTypes.recordName AS 'Record Type', 
                invNo as 'Invoice No.', 
                invDate as 'Invoice Date', 
                accNo as 'Summit Account No.',
                db_accountNo.name as 'Company Name', 
                contract as 'Contract No.', 
                taxStatus as 'Tax Status', 
                netVal as 'Net Value', 
                vat as 'V.A.T', 
                grossVal as 'Gross Value',
                paycert as 'Payment Certificate No.', 
                period as 'Period', 
                paydate as 'Anticipated Payment Date', 
                db_qs.name as 'QS record sent to', 
                sentDate as 'Date sent to QS',
                db_sentMethod.name as 'Sent Via', 
                returnedDate as 'Date Returned', 
                r.deleted as 'Removed', 
                lastModified as 'Last Modified', 
                creationDate as 'Date Created', 
                db_users.name as 'Creation User',
                p.date as 'Date Paid'
            --  Main Table to Reference --
                FROM db_records as r
            -- Proceed with SQL JOINs -- 
                JOIN db_recTypes
                ON db_recTypes.recordID = r.recType
                Join db_accountNo
                ON db_accountNo.com_id = r.accNo
                Join db_qs
                On db_qs.q_id = r.sentTo
                JOIN db_sentMethod 
                On db_sentMethod.v_id = r.sentVia
                Join db_users
                On db_users.u_id = r.u_id
                FULL OUTER JOIN db_payments as p
                ON p.r_id = r.r_id
            -- Check For the following values
                WHERE 
                    (@logging_ref IS NULL OR logRef LIKE '%' + @logging_ref + '%')
                    AND (@summit_ac_no IS NULL OR accNo LIKE '%' + @summit_ac_no + '%') 
                    AND (@contract_no IS NULL OR contract LIKE '%' + @contract_no + '%')
                    AND (@invoice_no IS NULL OR invNo LIKE '%' + @invoice_no + '%') 
                    AND (@company IS NULL OR db_accountNo.name LIKE '%' + @company + '%')
                    AND (@paycert IS NULL OR payCert LIKE '%' + @paycert + '%') 
                    AND (@record_type IS NULL OR recType = @record_type) 
                    AND (@qs IS NULL OR db_qs.name LIKE '%' + @qs + '%')
                    AND r.deleted = 0
        END
    ELSE
        Begin
            IF @state = 2
                BEGIN
                    SELECT TOP(@records) 
                    -- Reference Columns - Used to identify a Record --
                        r.r_id AS 'ref',  
                        grossVal AS 'gross', 
                        payCert AS 'cert', 
                    -- Data Columns for Display --
                        logRef as 'Logging Reference', 
                        db_recTypes.recordName AS 'Record Type', 
                        invNo as 'Invoice No.', 
                        invDate as 'Invoice Date', 
                        accNo as 'Summit Account No.',
                        db_accountNo.name as 'Company Name', 
                        contract as 'Contract No.', 
                        taxStatus as 'Tax Status', 
                        netVal as 'Net Value', 
                        vat as 'V.A.T', 
                        grossVal as 'Gross Value',
                        paycert as 'Payment Certificate No.', 
                        period as 'Period', 
                        paydate as 'Anticipated Payment Date', 
                        db_qs.name as 'QS record sent to', 
                        sentDate as 'Date sent to QS',
                        db_sentMethod.name as 'Sent Via', 
                        returnedDate as 'Date Returned', 
                        r.deleted as 'Removed', 
                        lastModified as 'Last Modified', 
                        creationDate as 'Date Created', 
                        db_users.name as 'Creation User',
                        p.date as 'Date Paid'
                    --  Main Table to Reference --
                        FROM db_records as r
                    -- Proceed with SQL JOINs -- 
                        JOIN db_recTypes
                        ON db_recTypes.recordID = r.recType
                        Join db_accountNo
                        ON db_accountNo.com_id = r.accNo
                        Join db_qs
                        On db_qs.q_id = r.sentTo
                        JOIN db_sentMethod 
                        On db_sentMethod.v_id = r.sentVia
                        Join db_users
                        On db_users.u_id = r.u_id
                        FULL OUTER JOIN db_payments as p
                        ON p.r_id = r.r_id
                    -- Check For the following values
                        WHERE 
                            (@logging_ref IS NULL OR logRef LIKE '%' + @logging_ref + '%')
                            AND (@summit_ac_no IS NULL OR accNo LIKE '%' + @summit_ac_no + '%') 
                            AND (@contract_no IS NULL OR contract LIKE '%' + @contract_no + '%')
                            AND (@invoice_no IS NULL OR invNo LIKE '%' + @invoice_no + '%') 
                            AND (@company IS NULL OR db_accountNo.name LIKE '%' + @company + '%')
                            AND (@paycert IS NULL OR payCert LIKE '%' + @paycert + '%') 
                            AND (@record_type IS NULL OR recType = @record_type) 
                            AND (@qs IS NULL OR db_qs.name LIKE '%' + @qs + '%')
                            AND p.date <> null
                END
            ELSE
                BEGIN
                    IF @state = 3
                        BEGIN
                            SELECT TOP(@records) 
                            -- Reference Columns - Used to identify a Record --
                                r.r_id AS 'ref',  
                                grossVal AS 'gross', 
                                payCert AS 'cert', 
                            -- Data Columns for Display --
                                logRef as 'Logging Reference', 
                                db_recTypes.recordName AS 'Record Type', 
                                invNo as 'Invoice No.', 
                                invDate as 'Invoice Date', 
                                accNo as 'Summit Account No.',
                                db_accountNo.name as 'Company Name', 
                                contract as 'Contract No.', 
                                taxStatus as 'Tax Status', 
                                netVal as 'Net Value', 
                                vat as 'V.A.T', 
                                grossVal as 'Gross Value',
                                paycert as 'Payment Certificate No.', 
                                period as 'Period', 
                                paydate as 'Anticipated Payment Date', 
                                db_qs.name as 'QS record sent to', 
                                sentDate as 'Date sent to QS',
                                db_sentMethod.name as 'Sent Via', 
                                returnedDate as 'Date Returned', 
                                r.deleted as 'Removed', 
                                lastModified as 'Last Modified', 
                                creationDate as 'Date Created', 
                                db_users.name as 'Creation User',
                                p.date as 'Date Paid'
                            --  Main Table to Reference --
                                FROM db_records as r
                            -- Proceed with SQL JOINs -- 
                                JOIN db_recTypes
                                ON db_recTypes.recordID = r.recType
                                Join db_accountNo
                                ON db_accountNo.com_id = r.accNo
                                Join db_qs
                                On db_qs.q_id = r.sentTo
                                JOIN db_sentMethod 
                                On db_sentMethod.v_id = r.sentVia
                                Join db_users
                                On db_users.u_id = r.u_id
                                FULL OUTER JOIN db_payments as p
                                ON p.r_id = r.r_id
                            -- Check For the following values
                                WHERE 
                                    (@logging_ref IS NULL OR logRef LIKE '%' + @logging_ref + '%')
                                    AND (@summit_ac_no IS NULL OR accNo LIKE '%' + @summit_ac_no + '%') 
                                    AND (@contract_no IS NULL OR contract LIKE '%' + @contract_no + '%')
                                    AND (@invoice_no IS NULL OR invNo LIKE '%' + @invoice_no + '%') 
                                    AND (@company IS NULL OR db_accountNo.name LIKE '%' + @company + '%')
                                    AND (@paycert IS NULL OR payCert LIKE '%' + @paycert + '%') 
                                    AND (@record_type IS NULL OR recType = @record_type) 
                                    AND (@qs IS NULL OR db_qs.name LIKE '%' + @qs + '%')
                                    AND p.date = null
                        END
                    ELSE
                        Begin
                            SELECT TOP(@records) 
                            -- Reference Columns - Used to identify a Record --
                                r.r_id AS 'ref',  
                                grossVal AS 'gross', 
                                payCert AS 'cert', 
                            -- Data Columns for Display --
                                logRef as 'Logging Reference', 
                                db_recTypes.recordName AS 'Record Type', 
                                invNo as 'Invoice No.', 
                                invDate as 'Invoice Date', 
                                accNo as 'Summit Account No.',
                                db_accountNo.name as 'Company Name', 
                                contract as 'Contract No.', 
                                taxStatus as 'Tax Status', 
                                netVal as 'Net Value', 
                                vat as 'V.A.T', 
                                grossVal as 'Gross Value',
                                paycert as 'Payment Certificate No.', 
                                period as 'Period', 
                                paydate as 'Anticipated Payment Date', 
                                db_qs.name as 'QS record sent to', 
                                sentDate as 'Date sent to QS',
                                db_sentMethod.name as 'Sent Via', 
                                returnedDate as 'Date Returned', 
                                r.deleted as 'Removed', 
                                lastModified as 'Last Modified', 
                                creationDate as 'Date Created', 
                                db_users.name as 'Creation User',
                                p.date as 'Date Paid'
                            --  Main Table to Reference --
                                FROM db_records as r
                            -- Proceed with SQL JOINs -- 
                                JOIN db_recTypes
                                ON db_recTypes.recordID = r.recType
                                Join db_accountNo
                                ON db_accountNo.com_id = r.accNo
                                Join db_qs
                                On db_qs.q_id = r.sentTo
                                JOIN db_sentMethod 
                                On db_sentMethod.v_id = r.sentVia
                                Join db_users
                                On db_users.u_id = r.u_id
                                FULL OUTER JOIN db_payments as p
                                ON p.r_id = r.r_id
                            -- Check For the following values
                                WHERE 
                                    (@logging_ref IS NULL OR logRef LIKE '%' + @logging_ref + '%')
                                    AND (@summit_ac_no IS NULL OR accNo LIKE '%' + @summit_ac_no + '%') 
                                    AND (@contract_no IS NULL OR contract LIKE '%' + @contract_no + '%')
                                    AND (@invoice_no IS NULL OR invNo LIKE '%' + @invoice_no + '%') 
                                    AND (@company IS NULL OR db_accountNo.name LIKE '%' + @company + '%')
                                    AND (@paycert IS NULL OR payCert LIKE '%' + @paycert + '%') 
                                    AND (@record_type IS NULL OR recType = @record_type) 
                                    AND (@qs IS NULL OR db_qs.name LIKE '%' + @qs + '%')
                                    AND r.deleted = 1
                        END
                END
        END

END

似乎导致此问题的表是db_payments,因为这是引用记录是否已支付的表。例如(如果该记录尚未付款,则此处不存在)

db_payments 布局为:

p_id  -  int  - Auto Increment
r_id  -  int  - Link to db_records
date  - date  - date paid

如果您需要db_records 表的布局,我可以发送。

更改的查询

现在我收到以下错误:

Msg 102, Level 15, State 1, Line 52
Incorrect syntax near 'r'.

程序如下:

USE [Sub-Con-Dev]
GO
/****** Object:  StoredProcedure [dbo].[SearchSQL]    Script Date: 22/02/2016 09:17:28 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:      Marcus Gloria
-- Create date: 27/01/2016
-- Description: Gets Search Results
-- =============================================
ALTER PROCEDURE [dbo].[SearchSQL]
    -- Add the parameters for the stored procedure here
    @logging_ref as varchar(50) = NULL,
    @summit_ac_no as varchar(50) = NULL,
    @contract_no as varchar(50) = NULL,
    @invoice_no as varchar(50) = NULL,
    @company as varchar(50) = NULL,
    @paycert as varchar(50) = NULL,
    @record_type as integer = NULL,
    @qs as varchar(50) = NULL,
    @records as int = NULL,
    @state as int = NULL
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    DECLARE @SQL NVARCHAR(MAX) = N'
        SELECT TOP(@records)
            r.r_id as ref,
            grossVal as gross,
            payCert as cert, 
            p.p_id as paid,
            -- Data Columns for Display --
                logRef as "Logging Reference", 
                db_recTypes.recordName AS "Record Type", 
                invNo as "Invoice No.", 
                invDate as "Invoice Date", 
                accNo as "Summit Account No.",
                db_accountNo.name as "Company Name", 
                contract as "Contract No.", 
                taxStatus as "Tax Status", 
                netVal as "Net Value", 
                vat as "V.A.T", 
                grossVal as "Gross Value",
                paycert as "Payment Certificate No.", 
                period as "Period", 
                paydate as "Anticipated Payment Date", 
                db_qs.name as "QS record sent to", 
                sentDate as "Date sent to QS",
                db_sentMethod.name as "Sent Via", 
                returnedDate as "Date Returned", 
                r.deleted as "Removed", 
                lastModified as "Last Modified", 
                creationDate as "Date Created", 
                db_users.name as "Creation User",
                p.date as "Date Paid"
            --  Main Table to Reference --
                FROM db_records as r
            -- Proceed with SQL JOINs -- 
                JOIN db_recTypes
                ON db_recTypes.recordID = r.recType
                Join db_accountNo
                ON db_accountNo.com_id = r.accNo
                Join db_qs
                On db_qs.q_id = r.sentTo
                JOIN db_sentMethod 
                On db_sentMethod.v_id = r.sentVia
                Join db_users
                On db_users.u_id = r.u_id
                FULL OUTER JOIN db_payments as p
                ON p.r_id = r.r_id
                WHERE 1 = 1 '
                 IF (@logging_ref IS NOT NULL) SET @SQL = @SQL + CHAR(13) + CHAR(10) + 'AND logRef LIKE ''%'' + @logging_ref'
                 IF (@summit_ac_no IS NOT NULL) SET @SQL = @SQL + CHAR(13) + CHAR(10) + 'AND accNo LIKE ''%'' + @summit_ac_no'
                 IF (@contract_no IS NOT NULL) SET @SQL = @SQL + CHAR(13) + CHAR(10) + 'AND contract LIKE ''%'' + @contract_no'
                 IF (@invoice_no IS NOT NULL) SET @SQL = @SQL + CHAR(13) + CHAR(10) + 'AND invNo LIKE ''%'' + @invoice_no'
                 IF (@company IS NOT NULL) SET @SQL = @SQL + CHAR(13) + CHAR(10) + 'AND db_accountNo.name LIKE ''%'' + @company'
                 IF (@paycert IS NOT NULL) SET @SQL = @SQL + CHAR(13) + CHAR(10) + 'AND payCert LIKE ''%'' + @paycert'
                 IF (@record_type IS NOT NULL) SET @SQL = @SQL + CHAR(13) + CHAR(10) + 'AND recType LIKE ''%'' + @record_type'
                 IF (@qs IS NOT NULL) SET @SQL = @SQL + CHAR(13) + CHAR(10) + 'AND db_qs.name LIKE ''%'' + @qs'

                DECLARE @lastToken NVARCHAR(100) =
                    (
                    CASE
                        WHEN @state = 1 THEN 'r.deleted = 0'
                        WHEN @state = 2 THEN 'p.date IS NOT NULL' 
                        WHEN @state = 3 THEN 'p.date IS NULL'
                        WHEN @state = 4 THEN 'r.deleted =1'
                    END
                    )
                SET  @SQL = @SQL + CHAR(13) + CHAR(10) + @lastToken

            EXEC sp_executesql @SQL,
                N'@logging_ref as varchar(50), @summit_ac_no as varchar(50) = NULL,
                @contract_no as varchar(50), @invoice_no as varchar(50), @company as varchar(50),
                @paycert as varchar(50), @record_type as integer, @qs as varchar(50),
                @records as int, @state as int',
                @records = @records,
                @logging_ref = @logging_ref,
                @summit_ac_no = @summit_ac_no,
                @contract_no = @contract_no,
                @invoice_no = @invoice_no,
                @company = @company,
                @paycert = @paycert,
                @record_type = @record_type,
                @qs = @qs,
                @records = @records,
                @state = @state     
        END

【问题讨论】:

  • 您说The table which appears to be causing this issue ,但实际上并没有说this issue 是什么。什么工作不正常?
  • 代替连接,或者在这种情况下full outer join,尝试left join
  • 你把它放在一起的方式是一个性能定时炸弹。在某些时候(并且它会随机发生),性能会降低一些可怕的东西。这里有两件事会混淆查询引擎。您有一个捕获所有查询并且您有多个执行路径。好消息是这可以修复。 Gail Shaw 有一篇关于这两个主题的精彩文章。您可以在程序中将逻辑合并在一起。sqlinthewild.co.za/index.php/2009/03/19/catch-all-queriessqlinthewild.co.za/index.php/2009/09/15/…

标签: sql sql-server join stored-procedures


【解决方案1】:

除了使用 LEFT JOIN 不要错过记录的建议之外,我强烈建议您使用动态 SQL 来简化您的过程。现在写的,违反了DRY principle,很难读懂。

由于您的代码在所有分支上都执行相同的操作,并且还使用了许多过滤器(至少实际上是这样),因此动态 SQL 有很大的机会表现得比过多的 ORs 更好。此外,程序要小得多,过滤器或逻辑的任何更改都可以轻松实现。

注意:未经实际测试

CREATE PROCEDURE [dbo].[SearchSQL]
    -- Add the parameters for the stored procedure here
    @logging_ref as varchar(50) = NULL,
    @summit_ac_no as varchar(50) = NULL,
    @contract_no as varchar(50) = NULL,
    @invoice_no as varchar(50) = NULL,
    @company as varchar(50) = NULL,
    @paycert as varchar(50) = NULL,
    @record_type as integer = NULL,
    @qs as varchar(50) = NULL,
    @records as int = NULL,
    @state as int = NULL
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    -- no need for alias quotes if they are a full word
    -- some comments omitted for brevity
    DECLARE @SQL NVARCHAR(MAX) = N'
        SELECT TOP(@records) 
            r.r_id AS ref, grossVal AS gross, payCert AS cert, 
            -- omitted for brevity
        FROM db_records as r
            JOIN db_recTypes ON db_recTypes.recordID = r.recType
            Join db_accountNo ON db_accountNo.com_id = r.accNo
            Join db_qs On db_qs.q_id = r.sentTo
            JOIN db_sentMethod On db_sentMethod.v_id = r.sentVia
            Join db_users On db_users.u_id = r.u_id
            LEFT JOIN db_payments as p ON p.r_id = r.r_id
            WHERE 1 = 1 '

        IF (@logging_ref IS NOT NULL) SET @SQL = @SQL + CHAR(13) + CHAR(10) + 'AND logRef LIKE ''%'' + @logging_ref'
        IF (@summit_ac_no IS NOT NULL) SET @SQL = @SQL + CHAR(13) + CHAR(10) + 'AND accNo LIKE ''%'' + @summit_ac_no'
        -- other filters come here
        IF (@qs IS NOT NULL) SET @SQL = @SQL + CHAR(13) + CHAR(10) + 'AND db_qs = @qs'

        DECLARE @lastToken NVARCHAR(100) = 
            (CASE WHEN @state = 1 THEN 'r.deleted = 0'
                  WHEN @state = 2 THEN 'p.date <> null'
                  WHEN @state = 3 THEN 'p.date IS NULL'         -- = NULL is not ok
                  WHEN @state = 4 THEN 'r.deleted = 1'
            END)                                                -- what happens if @state is not 1, 2, 3 or 4?

        SET @SQL = @SQL + CHAR(13) + CHAR(10) + @lastToken

        -- actual execution - PRINT @SQL may be used to check if the query is correct
        EXEC sp_executesql @SQL,
            N'@records INT, @logging_ref as varchar(50), @summit_ac_no as varchar(50) = NULL,
                @contract_no as varchar(50), @invoice_no as varchar(50), @company as varchar(50),
                @paycert as varchar(50), @record_type as integer, @qs as varchar(50),
                @records as int, @state as int',
            @records = @records, @logging_ref = @logging_ref, -- and so on
    END
END

【讨论】:

  • 嗨,Alexei,我创建了以下查询,但出现以下错误:Msg 214, Level 16, State 3, Procedure sp_executesql, Line 3 Procedure expects parameter '@params' of type 'ntext/nchar/nvarchar'.
  • @MarcusITSupport - 抱歉,我在 EXEC sp_executesql @SQL、N'@records INT、@logging_ref as varchar(50) 中遗漏了一个 N,...和 ​​TSQL在这种情况下,不会将 VARCHAR 隐式转换为 NVARCHAR
猜你喜欢
  • 2016-07-22
  • 1970-01-01
  • 2014-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-29
  • 2020-09-25
  • 1970-01-01
  • 2014-10-25
相关资源
最近更新 更多