Problem using CTE

  • Can anyone explain why, when selecting data into a CTE and then using the results in the CTE to further narrow the data using a field comparison (<>, not equal), the CTE conducts the comparison on all the data in the table instead of only comparing the data used to populate the CTE in the first place?

    A little background on my data.

    I have the following fields (and data types) with example data:

    Sequence# (int)Member# (char) Group# (char)
    2300AB00010002300AB0001
    23012301PP0018

    The Member# is derived either by a combination of  the Group# + Sequence# (with some leading zeros added to the Seq#), or by adding leading leading zeros to the Member# depending on what Group a member is in.

    What I was trying to accomplish with a CTE is to select data by Group#, then compare Member# with the Sequence# to identify any that do not match.  The obvious issue is converting the CHAR datatype to INT.

    When I do a simple SELECT * from dbo.TableName WHERE Group# = 'PP0018', I do in fact only get those members in that group.  And it is significant to know that the member numbers for this group are all numeric (no letters).

    When I construct my CTE, it is as follows:

       WITH Groups AS (
               SELECT Sequence#, Member#, Group#
               WHERE Group# = 'PP0018'
       )

       SELECT *
       FROM Groups
       WHERE Sequence# <> CAST(Member# AS INT)
       ORDER BY Sequence#

    Again, when I execute the SELECT statement within the CTE, I only get data in Group# PP0018. 

    HOWEVER, when I execute the entire query, I am getting an error because for some unknown reason it appears the CTE is executing the logic in the WHERE clause on all of the data in the table and not just what I selected in the CTE query.  It errors because it can't convert Member#'s that are alphanumeric to integers (which shouldn't be looked at since they were excluded in the CTE).

    If I construct this query using a temp table, I have no issues and I get the results I want.

    This is quite perplexing.

    Thank you for your time!

  • Cast Sequence# as a char and don't convert member# to an int to get around this. At least that's what I would do.  There is no FROM in your CTE, example, I'm assuming just a bad cut and paste here since you said when you ran the SQL in the CTE you got what you expected.

    -------------------------------------------------------------
    we travel not to escape life but for life not to escape us
    Don't fear failure, fear regret.

  • Yes, I do have the FROM in the CTE, just didn't get it in here.

    I didn't think to try to cast the int as a char.  I'll give that a try.

    Thanks!

  • wegrob - Monday, March 25, 2019 1:45 PM

    Can anyone explain why, when selecting data into a CTE and then using the results in the CTE to further narrow the data using a field comparison (<>, not equal), the CTE conducts the comparison on all the data in the table instead of only comparing the data used to populate the CTE in the first place?

    A little background on my data.

    I have the following fields (and data types) with example data:

    Sequence# (int)Member# (char) Group# (char)
    2300AB00010002300AB0001
    23012301PP0018

    The Member# is derived either by a combination of  the Group# + Sequence# (with some leading zeros added to the Seq#), or by adding leading leading zeros to the Member# depending on what Group a member is in.

    What I was trying to accomplish with a CTE is to select data by Group#, then compare Member# with the Sequence# to identify any that do not match.  The obvious issue is converting the CHAR datatype to INT.

    When I do a simple SELECT * from dbo.TableName WHERE Group# = 'PP0018', I do in fact only get those members in that group.  And it is significant to know that the member numbers for this group are all numeric (no letters).

    When I construct my CTE, it is as follows:

       WITH Groups AS (
               SELECT Sequence#, Member#, Group#
               WHERE Group# = 'PP0018'
       )

       SELECT *
       FROM Groups
       WHERE Sequence# <> CAST(Member# AS INT)
       ORDER BY Sequence#

    Again, when I execute the SELECT statement within the CTE, I only get data in Group# PP0018. 

    HOWEVER, when I execute the entire query, I am getting an error because for some unknown reason it appears the CTE is executing the logic in the WHERE clause on all of the data in the table and not just what I selected in the CTE query.  It errors because it can't convert Member#'s that are alphanumeric to integers (which shouldn't be looked at since they were excluded in the CTE).

    If I construct this query using a temp table, I have no issues and I get the results I want.

    This is quite perplexing.

    Thank you for your time!

    There is no guarantee that SQL will process the CTE first.  If you need that guarantee, then create a #TempTable, and populate it with the values from the CTE.  Then use the #TempTable as your new source.

  • That is interesting to know that SQL may not process the CTE first.  I did use a temp table instead of the CTE for this reason.  Just an interesting CTE 'flaw'.

  • wegrob - Tuesday, March 26, 2019 6:27 AM

    That is interesting to know that SQL may not process the CTE first.  I did use a temp table instead of the CTE for this reason.  Just an interesting CTE 'flaw'.

    No necessarily a flaw, just a decision that SQL Server makes while processing the query.  You might see the same issue if you made the CTE a view and used it in your query.

  • We're starting from a fundamental problem in the premise:

    Can anyone explain why, when selecting data into a CTE and then using the results in the CTE to further narrow the data using a field comparison

    A common table expression is not a table. It's an expression. That's the single most important thing to understand when you start to use CTEs. You're defining an expression, similar to a view or a sub-query. You're not defining a table like with a table variable or temporary table. No temporary storage space is created for the CTE. No data gets moved into the non-existent temporary storage space. It's just a query. It's a unique query because you can define it once and then reuse it multiple times, including in recursion. However, it's just a query that you're adding to the other queries that you're defining. You won't see the behavior that you're asking for because that's not how it works. You can validate this yourself by looking at the execution plans for a CTE and a query using a sub-select or a view. They'll all get defined exactly the same way by the optimizer (assuming the same fundamental code in all three).

    "The credit belongs to the man who is actually in the arena, whose face is marred by dust and sweat and blood"
    - Theodore Roosevelt

    Author of:
    SQL Server Execution Plans
    SQL Server Query Performance Tuning

  • Lynn Pettis - Tuesday, March 26, 2019 7:13 AM

    wegrob - Tuesday, March 26, 2019 6:27 AM

    That is interesting to know that SQL may not process the CTE first.  I did use a temp table instead of the CTE for this reason.  Just an interesting CTE 'flaw'.

    No necessarily a flaw, just a decision that SQL Server makes while processing the query.  You might see the same issue if you made the CTE a view and used it in your query.

    Creating a view instead of a CTE was one thing I tried this morning.  You are correct!  I encountered the same issue.  I even thought a materialized view would work.  It did not.

  • Try it this way:


    SELECT *
    FROM (
      SELECT Sequence#, Member#, Group#
      WHERE Group# = 'PP0018'
    ) AS iq1
    WHERE Sequence# <> CASE WHEN Member# NOT LIKE '%[^0-9]%' THEN Member# ELSE -1 END
    ORDER BY Sequence#

    SQL DBA,SQL Server MVP(07, 08, 09) "It's a dog-eat-dog world, and I'm wearing Milk-Bone underwear." "Norm", on "Cheers". Also from "Cheers", from "Carla": "You need to know 3 things about Tortelli men: Tortelli men draw women like flies; Tortelli men treat women like flies; Tortelli men's brains are in their flies".

  • wegrob - Monday, March 25, 2019 3:02 PM

    Yes, I do have the FROM in the CTE, just didn't get it in here.

    I didn't think to try to cast the int as a char.  I'll give that a try.

    Thanks!

    Did you try this?  What was the result?

    -------------------------------------------------------------
    we travel not to escape life but for life not to escape us
    Don't fear failure, fear regret.

  • below86 - Tuesday, March 26, 2019 12:42 PM

    wegrob - Monday, March 25, 2019 3:02 PM

    Yes, I do have the FROM in the CTE, just didn't get it in here.

    I didn't think to try to cast the int as a char.  I'll give that a try.

    Thanks!

    Did you try this?  What was the result?

    I did not.  I used a temp table to make the magic happen.

Viewing 11 posts - 1 through 11 (of 11 total)

You must be logged in to reply to this topic. Login to reply