sql-serverxmlxml-parsingqxmlquery

SQL Server xml query doesn't return expected result


I have a column in my database FlowDetailParameter with XML Type .My table has one column FlowDetailParameter and 3 rows with these data :

row 1

<ArrayOfFlowDetailParameters xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <FlowDetailParameters>
    <DepartmentId>7</DepartmentId>
    <UserId>6</UserId>
    <Username>4</Username>
    <FullName>کارشناس  معاینه فنی</FullName>
    <ConfirmDateTime>2018-11-01T10:45:29.7371421+03:30</ConfirmDateTime>
    <Comment>اولین IP تاییدی</Comment>
    <Status>Accept</Status>
  </FlowDetailParameters>
  <FlowDetailParameters>
    <DepartmentId>3</DepartmentId>
    <UserId xsi:nil="true" />
    <Username />
    <FullName />
    <ConfirmDateTime xsi:nil="true" />
    <Comment />
    <Status>Pending</Status>
    <AttachmentId />
  </FlowDetailParameters>
</ArrayOfFlowDetailParameters>

row 2

<ArrayOfFlowDetailParameters xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <FlowDetailParameters>
    <DepartmentId>7</DepartmentId>
    <UserId>6</UserId>
    <Username>4</Username>
    <FullName>کارشناس  معاینه فنی</FullName>
    <ConfirmDateTime>2018-11-01T10:45:40.437481+03:30</ConfirmDateTime>
    <Comment>دومین IP تاییدی</Comment>
    <Status>Accept</Status>
  </FlowDetailParameters>
  <FlowDetailParameters>
    <DepartmentId>3</DepartmentId>
    <UserId xsi:nil="true" />
    <Username />
    <FullName />
    <ConfirmDateTime xsi:nil="true" />
    <Comment />
    <Status>Pending</Status>
    <AttachmentId />
  </FlowDetailParameters>
</ArrayOfFlowDetailParameters>


row 3

<ArrayOfFlowDetailParameters xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <FlowDetailParameters>
    <DepartmentId>7</DepartmentId>
    <UserId xsi:nil="true" />
    <Username />
    <FullName />
    <ConfirmDateTime xsi:nil="true" />
    <Comment />
    <Status>Pending</Status>
    <AttachmentId />
  </FlowDetailParameters>
  <FlowDetailParameters>
    <DepartmentId>3</DepartmentId>
    <UserId xsi:nil="true" />
    <Username />
    <FullName />
    <ConfirmDateTime xsi:nil="true" />
    <Comment />
    <Status />
    <AttachmentId />
  </FlowDetailParameters>
</ArrayOfFlowDetailParameters>

I want to find the departmentId=3 and status=Pending ,so the expected result should return 2 rows .So here is my query :

    select  Requests.*   from Requests

 where    
  ((SELECT count(*)   
 FROM Requests t
    CROSS APPLY t.FlowDetailParameter.nodes ('/ArrayOfFlowDetailParameters/FlowDetailParameters') x(v)
where    x.v.value('(DepartmentId/text())[1]', 'bigint')=3   and  x.v.value('(Status/text())[1]', 'varchar(50)') = 'Pending') >0)

But my query returns all rows (3 rows) why ?


Solution

  • First to answer your question "why?":

    Your sub-query is not a correlated sub-query. There is no connection to the current row from the outer SELECT. So - assuming there is at least 1 row fulfilling your condition - this will always provide a count>0.

    Although your approach can be corrected, I'd suggest to use the XML-method .exist() and provide the filter as XPath/XQuery:

    SELECT * 
    FROM Requests r
    WHERE r.FlowDetailParameter.exist(N'/ArrayOfFlowDetailParameters
                                        /FlowDetailParameters[(DepartmentId/text())[1]=3 
                                                              and (Status/text())[1]="Pending"]')=1;
    

    This will check for the existance of any <FlowDetailParameters> for the given condition.

    If you want to introduce the filter dynamically, you can use sql:variable() or sql:column() instead of 3 and "Pending"

    DECLARE @depId INT=3;
    DECLARE @status VARCHAR(100)='Pending';
    
    SELECT * 
    FROM Requests r
    WHERE r.FlowDetailParameter.exist(N'/ArrayOfFlowDetailParameters
                                        /FlowDetailParameters[(DepartmentId/text())[1]=sql:variable("@depId")
                                                              and (Status/text())[1]=sql:variable("@status")]')=1