2008-12-10 3 views
0

Несколько лет назад я работал над системой, в которой числовой первичный ключ хранился в столбце varchar [SQL Server], поэтому я быстро отклеился при запросе с помощью между оператором:Проблемы с хранением числовых данных в текстовых столбцах - SELECT ... BETWEEN

SELECT ID FROM MyTable WHERE ID BETWEEN 100 AND 110; 

Результаты:

100 
102 
103 
109 
110 
11 

Это был просто плохой дизайн. Тем не менее, я работаю над сторонней ERP-системой, которая, как вы можете себе представить, должна быть универсальной и гибкой; таким образом, у нас есть разные таблицы, в которых представлены буквенно-цифровые поля, в которых бизнес использует только численные значения - поэтому могут возникать подобные проблемы.

Я предполагаю, что это достаточно распространенная проблема; У меня достаточно простое решение, но мне любопытно, как другие подходят к таким проблемам.

Мой простое решение:

SELECT ID FROM MyTable 
WHERE ID BETWEEN iStartValue AND iEndValue 
AND (LENGTH(ID) = LENGTH(iStartValue) 
OR LENGTH(ID) = LENGTH(iEndValue)); 

Как вы, возможно, сказать, что это система Oracle, но я обычно работает в SQL Server - поэтому, возможно, база данных агностик решения являются более предпочтительными.

Редактировать 1: Поцарапать это - я не понимаю, почему запатентованные решения также не приветствуются.

Редактировать 2: Спасибо за все ответы. Я не уверен, разочарован ли я, что нет очевидного и сложного решения, но я, соответственно, рад, что я не вижу ничего очевидного!

Я думаю, что я по-прежнему предпочитаю собственное решение; это просто, и это работает - есть ли причина, почему я не должен ее использовать? Я не могу поверить, что это много, если вообще возможно, менее эффективно, чем другие предлагаемые решения.

Я понимаю, что в идеальном мире эта проблема не будет существовать; но, к сожалению, я не работаю в идеальном мире, и часто это случай, когда вы делаете лучшее из плохой ситуации.

+0

Я не использовал Oracle через год или около того, но если вы получаете ошибки преобразования при кастинге, вы не получите их, если в вашем заявлении указано неявное преобразование? – 2008-12-10 17:40:49

+0

LENGTH - это функция, предназначенная для работы с строками, поэтому нет. – 2008-12-12 15:13:49

ответ

0

В конце концов, я придерживался своего собственного решения (см. OP). Спасибо за ваши усилия.

7

Если вы уверены, что значения в ID являются числовыми только, почему не только изгонять их

WHERE CAST(ID as int) BETWEEN iStartValue AND iEndValue 

EDIT 1: Продолжение метода литья, который должен работать, чтобы использовать подзапрос чтобы вытащить все числовые записи. Обратите внимание: я не думаю, что этот метод лучше, чем тот, который предлагается выше, я включаю его, поскольку он отвечает на проблему !!!

SELECT ID 
FROM (
    SELECT ID 
    FROM MyTable 
    WHERE ISNUMERIC(ID) = 1 
    AND CHARINDEX ('.', ID) = 0 
    AND CHARINDEX ('-', ID) = 0 
    ) a 
WHERE CONVERT(bigint, ID) BETWEEN 0 AND 12000 
ORDER BY LENGTH(ID) ASC, ID 

Проверка на "-" и "." символов не требуется. Я предполагаю, что ваши идентификаторы не могут быть отрицательными или десятичными.

+0

В первом примере были исторические данные, которые мешали этому - БД использовалась более чем одной аппликацией, и мы не имели возможности ее изменить. Во втором случае многие из таблиц являются универсальными/многопользовательскими, поэтому в некоторых случаях альфы были разрешены/использованы в других частях приложения. – CJM 2008-12-10 15:00:32

+0

Кастинг таким образом предотвратит использование индексов. Добро пожаловать в TableScan. – 2008-12-10 15:01:45

+0

[В случае, если это не ясно - CASTing, когда в таблицах есть буквенно-цифровые данные, вызвала ошибку (Ошибка SQL: ORA-01722: недопустимый номер)] – CJM 2008-12-10 15:01:50

1

Как насчет отливки вместо этого.

SELECT ID FROM MyTable 
WHERE cast(ID as signed) BETWEEN cast(iStartValue as signed) AND cast(iEndValue as signed) 

Этот синтаксис задан как MySQL, но существуют аналогичные операторы CAST для T-SQl.

2

Я не знаю, может ли это работать в вашей ситуации, но ...

Как насчет добавления фактической числовой столбец таблицы, заполняются значением (SQL Server можно использовать вычисляемый столбец с сохраняемым индексом, установленным на нем)

В других поставщиках БД использовать другой механизм для заполнения (триггер, материализованное представление, и т.д.)

, а затем использовать этот столбец вместо VARCHAR один ...

1

Возможно LPAD (идентификатор, 12»«) будет работать для вас. Он должен делать все значения столбцов 12 широкими, с пробелами слева.

Также я был бы немного обеспокоен числами в столбцах varchar2.

Если вы делаете какую-либо цифру, например аналитику, вы можете получить исключение для не числовых данных.

1

Другим вариантом является то, что вы должны оставить свои номера нулями и использовать между ними оператор. По соображениям целесообразности, вероятно, лучше включить это как второе условие (так что возможные индексы еще могут быть использованы). Что-то вроде этого ...

SELECT ID FROM MyTable 
WHERE ID BETWEEN iStartValue AND iEndValue 
     And Right('0000000000' + ID, 10) Between iStartValue and iEndValue 

Я протестировал это на SQL Server и вернул правильные значения. Возможно, вам придется изменить это для работы с Oracle.