【问题标题】:how to use GETDATE() function with xml exist() function如何将 GETDATE() 函数与 xml exists() 函数一起使用
【发布时间】:2018-07-16 13:55:28
【问题描述】:

我有一个场景,我必须在查询的 where 子句中使用 xml exists() 函数。我必须将xml中的日期与当前日期进行比较。当我尝试使用 GETDATE() 函数时,出现以下错误。

XML 数据类型方法“exist”的参数 1 必须是字符串字面量。

请考虑以下查询以供参考。

SELECT 
       TRY_CONVERT(datetime,NULLIF(t.x.value('(./Expire)[1]','varchar(max)'), '')) as expiration_date
FROM VW_Analytics_Base_Facts(nolock) BaseFact
CROSS APPLY BaseFact.Fact.nodes ('/Fact/Grant') t(x)
WHERE TYPE = '/asset/portfolio/option' 
AND BaseFact.Fact.exist('./Expire[(text()[1] cast as xs:date?) le xs:date("' + CONVERT(NVARCHAR(max),CONVERT(date,GETDATE()))+'")]')=1

提前致谢。

以下是更新后的工作查询。

DECLARE @tbl TABLE (XmlCol xml)
INSERT INTO @tbl VALUES 
('<option>
            <OptionName>Option 1</OptionName>
            <grant>
                    <GrantName>Grant 1</GrantName>
                            <schedules>
                                            <schedule>
                                                            <scheduleID></scheduleID>
                                                            <scheduleName></scheduleName>
                                                            <scheduleDate>1/1/2018</scheduleDate>
                                                            <scheduleAmount></scheduleAmount>
                                            </schedule>
                                            <schedule>
                                                            <scheduleID></scheduleID>
                                                            <scheduleName></scheduleName>
                                                            <scheduleDate>2/1/2018</scheduleDate>
                                                            <scheduleAmount></scheduleAmount>
                                            </schedule>
                                            <schedule>
                                                            <scheduleID></scheduleID>
                                                            <scheduleName></scheduleName>
                                                            <scheduleDate>3/1/2018</scheduleDate>
                                                            <scheduleAmount></scheduleAmount>
                                            </schedule>
                            </schedules>
            </grant>
            <grant>
                            <GrantName>Grant 2</GrantName>
                            <schedules>
                                            <schedule>
                                                            <scheduleID></scheduleID>
                                                            <scheduleName></scheduleName>
                                                            <scheduleDate>1/1/2019</scheduleDate>
                                                            <scheduleAmount></scheduleAmount>
                                            </schedule>
                                            <schedule>
                                                            <scheduleID></scheduleID>
                                                            <scheduleName></scheduleName>
                                                            <scheduleDate>2/1/2019</scheduleDate>
                                                            <scheduleAmount></scheduleAmount>
                                            </schedule>
                                            <schedule>
                                                            <scheduleID></scheduleID>
                                                            <scheduleName></scheduleName>
                                                            <scheduleDate>3/1/2019</scheduleDate>
                                                            <scheduleAmount></scheduleAmount>
                                            </schedule>
                            </schedules>
            </grant>
        </option>'
)



SELECT e.XmlCol.value('(/option/OptionName)[1]', 'varchar(100)'),
        t.x.value('../.././GrantName[1]','varchar(100)') GrantName,
       t.x.value('(./scheduleDate)[1]', 'varchar(100)') scheduleDate
FROM @tbl e
       CROSS APPLY (SELECT CONVERT(date,GETDATE())) dt(today)
    cross apply e.XmlCol.nodes ('/option/grant/schedules/schedule') t(x)
WHERE e.XmlCol.exist('./scheduleDate[(text()[1] cast as xs:date?) le sql:column("dt.today")]')=1

我想根据计划日期过滤记录。 此查询是视图的一部分,因此我无法声明变量。

【问题讨论】:

    标签: sql xml


    【解决方案1】:

    下次请附加一个独立工作示例,其中包含 DDL、数据插入、您自己的代码和预期的输出。这将允许测试解决方案。

    在这种情况下,我必须猜测并发布未经测试:

    SELECT 
           TRY_CONVERT(datetime,NULLIF(t.x.value('(./Expire)[1]','varchar(max)'), '')) as expiration_date
    FROM VW_Analytics_Base_Facts(nolock) BaseFact
    CROSS APPLY (SELECT CONVERT(date,GETDATE())) dt(today)
    CROSS APPLY BaseFact.Fact.nodes ('/Fact/Grant') t(x)
    WHERE TYPE = '/asset/portfolio/option' 
    AND BaseFact.Fact.exist('./Expire[(text()[1] cast as xs:date?) le sql:column("dt.today")]')=1;
    

    此查询将再使用一个CROSS APPLY,以便将信息包含在结果集中。函数sql:column() 允许在XQuery 中使用结果集的列,而不会违反must-be-a-literal 规则(使用sql:variable() 来获取已声明变量的值)。

    更新

    您尽早应用过滤器的想法没有错。这在.nodes() 中作为谓词 效果更好。但是你必须依赖隐式强制转换,这是你不应该做的。

    表达式(text()[1] cast as xs:date?) 将使用系统的文化。 1/3/2018 之类的值可能会被视为 3 月 1 日或 1 月 3 日。因此,我建议将值读取为字符串并以适当的样式使用CONVERT:

    顺便说一句:我使用两次CROSS APPLY 和.nodes() 来避免向后导航 (../../)

    WITH DerivedTable AS
    (
        SELECT e.XmlCol.value('(/option/OptionName/text())[1]', 'varchar(100)') AS OptionName
              ,gr.value('(GrantName/text())[1]','varchar(100)') GrantName
              ,sch.value('(scheduleID/text())[1]', 'varchar(100)') scheduleID         --use appropriate type, might be "int"
              ,sch.value('(scheduleName/text())[1]', 'varchar(100)') scheduleName
              ,CONVERT(DATE,sch.value('(scheduleDate/text())[1]', 'varchar(100)'),110) scheduleDate     --fetch this as varchar, to avoid implicit casts
                                                                                                        --use CONVERT with the appropriate style to get a real date
                                                                                                        --110 is mdy, 103 is dmy
              ,sch.value('(scheduleAmount/text())[1]', 'varchar(100)') scheduleAmount --use appropriate type, might be "decimal(12,4)"
        FROM @tbl e
        CROSS APPLY e.XmlCol.nodes ('/option/grant') A(gr)
        CROSS APPLY A.gr.nodes('schedules/schedule') B(sch)
    )
    SELECT dt.*
    FROM DerivedTable dt
    --use a simple WHERE here
    

    这会将 XML 的内容作为派生表返回。使用简单的WHERE 过滤这个派生表。

    【讨论】:

    • 您好,先生,我已对查询进行了更改。你给的解决方案没有用。请看一看。
    • @RichiSharma 我将在大约 1 小时后看看这个... 一个简短的问题:XML 的创建是否在您的控制之下?如果是:以文化相关格式存储日期是一个非常糟糕的主意。如果您使用SET DATEFORMAT dmy; 或SET DATEFORMAT mdy;,您的查询将不会返回相同的结果。使用像 13/1/2018 这样的值,它可能会在另一个系统上失败...
    • 我无法控制 XML 创建。我所要做的就是创建一个视图来创建一个数据集。
    • 查询是视图的一部分。所以我不能像这样使用派生查询。我尝试尽早应用过滤器是为了尽量减少视图上的负载。目前,由于我正在解析 XML,因此查询非常繁重。这就是为什么我想尽早过滤记录。
    【解决方案2】:

    以下是对我有用的工作。

    SELECT e.XmlCol.value('(/option/OptionName)[1]', 'varchar(100)') OptionName,
        t.x.value('../.././GrantName[1]','varchar(100)') GrantName,
       t.x.value('(./scheduleDate)[1]', 'varchar(100)') scheduleDate
    FROM @tbl e
        CROSS APPLY e.XmlCol.nodes ('/option/grant/schedules/schedule') t(x)
    WHERE TRY_CONVERT(datetime,NULLIF(t.x.value('(./scheduleDate)[1]','varchar(max)'), '')) BETWEEN CONVERT(date,GETDATE()) AND DATEADD(YEAR, 1, GETDATE())
    

    【讨论】:

    • 如果可行,那就没问题...我不知道您是否检查了我更新的解决方案。它遵循相同的想法(获取所有值并使用简单的WHERE 过滤结果),但更优雅,更易于阅读并避免了一些性能杀手。如果你想坚持这一点,你应该 - 至少 - 将第三个参数添加到CONVERT。永远不要依赖日期值的隐式转换...
    • 非常感谢您的建议先生。真的很有帮助。
    • 如果我的回答“真的有帮助”,那么您很高兴使用您的投票权。接受和投票是表达谢谢的方式:-D
    猜你喜欢
    • 2021-06-05
    • 1970-01-01
    • 2019-08-20
    • 1970-01-01
    • 1970-01-01
    • 2018-11-20
    • 1970-01-01
    • 2011-01-15
    • 1970-01-01
    相关资源
    最近更新 更多