Как я могу заставить SQL для оценки соединения, только если значение может быть преобразовано в INT? - PullRequest
2 голосов
/ 27 августа 2011

У меня есть запрос, который использует несколько подзапросов.Это около 100 строк, поэтому я опущу это.Проблема заключается в том, что у меня есть несколько строк, возвращаемых как часть одного подзапроса, которые необходимо объединить с целочисленным значением из основного запроса.Вот так:

Select 
... columns ... 
from 
... tables ... 
        (
        select 
        ... column ... 
        from 
        ... tables ...  
        INNER JOIN core.Type mt 
                        on m.TypeID = mt.TypeID 
                    where dpt.[DataPointTypeName] = 'TheDataPointType'
                        and m.TypeID in (100008, 100009, 100738, 100739) 
                        and datediff(d, m.MeasureEntered, GETDATE()) <  365 -- only care about measures from past year 
                        and dp.DataPointValue <> ''
                        ) as subMdp                     
         ) as subMeas 
    on (subMeas.DataPointValue NOT LIKE '%[^0-9]%'  
        and subMeas.DataPointValue = cast(vcert.IDNumber as varchar(50))) -- THIS LINE
... more tables etc ...     

Проблема в том, что, если я достану cast(vcert.IDNumber as varchar(50))), он попытается сравнить значение типа 'daffodil' с числом, подобным 3245. Даже если точка данных, содержащая 'daffodil'является бесхозной записью, которую следует отфильтровать по INNER JOIN 4 строкам над ней.Это работает нормально, если я пытаюсь сравнить строку со строкой, но взрывается, если я пытаюсь сравнить строку с целым числом - даже если у меня есть предложение там, чтобы посмотреть только на вещи, которые можно преобразовать в целые числа: NOT LIKE '%[^0-9]%'.Если я специально отфильтрую запись, содержащую «нарцисс», тогда все в порядке.Если я переместу строку NOT LIKE в подзапрос, она все равно потерпит неудачу.Это как NOT LIKE оценивается последним независимо от того, что я делаю.

Таким образом, реальный вопрос заключается в том, почему SQL будет оценивать предложение JOIN, прежде чем оценивать предложение WHERE, содержащееся в подзапросе.Также, как я могу заставить его оценивать предложение JOIN только в том случае, если оцениваемое значение конвертируется в INT.Кроме того, почему это будет оценка записи, которая определенно не будет присутствовать после применения INNER JOIN.

Я понимаю, что здесь присутствует сильный элемент оптимизатора запросов voodoo .С другой стороны, я говорю это сделать INNER JOIN, а оптимизатор специально игнорирует это.Я хотел бы знать почему.

Ответы [ 4 ]

2 голосов
/ 29 августа 2011

Проблема, с которой вы столкнулись, обсуждается в этом элементе обратной связи на сайте подключения.

Хотя логически вы можете ожидать, что фильтр исключит любые значения DataPointValue, которые содержат любыечисловые символы SQL Server, по-видимому, упорядочивает операцию CAST в плане выполнения до того, как этот фильтр произойдет.Отсюда ошибка.

Пока Denali не выйдет вместе со своей функцией TRY_CONVERT, можно обернуть использование столбца в выражение case, которое повторяет ту же логику, что и фильтр.

2 голосов
/ 27 августа 2011

Таким образом, реальный вопрос заключается в том, почему SQL будет оценивать предложение JOIN перед оценкой предложения WHERE, содержащегося в подзапросе.

Потому что движки SQL должны вести себя так, как будто это то, что они делают. Они должны действовать так, как будто они строят рабочую таблицу из всех конструкторов таблиц в предложении FROM; выражения в предложении WHERE применяются к этой рабочей таблице.

Джо Селко много раз писал об этом в Usenet. Вот старая версия с более подробной информацией.

0 голосов
/ 29 августа 2011

Прежде всего

 NOT LIKE '%[^0-9]%'

не работает хорошо. Пример:

DECLARE @Int nvarchar(20)= ' 454 54'
SELECT  CASE WHEN @INT LIKE '%[^0-9]%' THEN 1 ELSE 0 END AS Is_Number
Result: 1

Но это не число!

Чтобы проверить, является ли это действительным значением типа int, следует использовать функцию ISNUMERIC . Давайте проверим это:

DECLARE @Int nvarchar(20)= ' 454 54'
SELECT ISNUMERIC(@int) Is_Int
Result:0

Результат верен.

Итак, вместо

NOT LIKE '%[^0-9]%'

попробуйте изменить это на

ISNUMERIC(subMeas.DataPointValue)=0

UPDATE

Как проверить, является ли значение целым числом? Сначала здесь :

WHERE ISNUMERIC(str) AND str NOT LIKE '%.%' AND str NOT LIKE '%e%' AND str NOT LIKE '%-%'

Второе:

CREATE Function dbo.IsInteger(@Value VarChar(18))
Returns Bit
As 
Begin

  Return IsNull(
     (Select Case When CharIndex('.', @Value) > 0 
                  Then Case When Convert(int, ParseName(@Value, 1)) <> 0
                            Then 0
                            Else 1
                            End
                  Else 1
                  End
      Where IsNumeric(@Value + 'e0') = 1), 0)

End
0 голосов
/ 27 августа 2011

Отфильтровать нечисловые записи в подзапросе или CTE

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...