Получение всех строк из таблицы, где столбец содержит только 0 - PullRequest
2 голосов
/ 22 апреля 2010

У меня маленькая проблема

Мне нужен SQL-запрос, который возвращает все строки, которые содержат только 0.

столбец определен как varchar2 (6) значения в столбце выглядят так:

Row      Value
1        0
2        00
3        00
4        100
5        bc00
6        000000
7        00000

Мое первое решение будет таким:

Oracle:
substr('000000' || COLUMN_NAME, -6) = '000000'

SQL Server:
right('000000' + COLUMN_NAME, 6) = '000000'

Есть ли другой способ? (он должен работать на обеих системах)

на выходе будет строка 1,2,3,6,7

Ответы [ 7 ]

3 голосов
/ 22 апреля 2010

Это самый простой:

select * from tbl where replace(col,'0','') = ''

Если вы не создадите вычисляемый столбец для этого выражения, вы можете выбрать индекс на основе функций (примечание: Oracle и Postgres уже поддерживают это; Sql Server с версии 2008, пока нет), чтобы сделать этот исполнитель:

create index ix_tbl on tbl(replace(col,'0',''))

[EDIT]

Я просто оставляю ответ ниже для потомков, я пытался объяснить, как заставить запрос использовать индекс из вычисляемого столбца.

Используйте это:

select * from tbl
where ISNUMERIC(col) = 1 and cast(col as int) = 0

Для ISNUMERIC потребностей в Oracle используйте это: http://www.oracle.com/technology/oramag/oracle/04-jul/o44asktom.html

[EDIT]

@ Charles, re: вычисляемый столбец в Oracle:

Для СУБД, которые поддерживают вычисляемый столбец, но не имеют сохраняемой опции, да, она будет вызывать функцию для каждой строки. Если он поддерживает постоянный столбец, он не будет вызывать функцию, у вас есть реальный столбец в таблице, который предварительно вычисляется из этой функции. Теперь, если данные могут заставить функцию вызывать исключение, есть два сценария.

Во-первых, если вы не указали постоянство, это позволит вам сохранить вычисляемый столбец (ALTER TABLE tbl ADD numeric_equivalent AS cast(col as int)), даже если результат из данных вызовет исключение, но вы не можете безоговорочно выбрать этот столбец, это вызовет исключение:

select * from tbl

исключение не возникнет:

select * from tbl where is_col_numeric = 1

это будет:

select * from tbl where numeric_equivalent = 0 and is_col_numeric = 1

это не будет (Sql Server поддерживает короткое замыкание ):

select * from tbl where is_col_numeric = 1 and numeric_equivalent = 0 

Для справки приведенный выше is_col_numeric был создан с использованием этого:

ALTER TABLE tbl ADD 
is_col_numeric  AS isnumeric(col)       

И это индекс is_col_numeric:

create index ix_is_col_numeric on tbl(is_col_numeric)

Теперь для второго сценария вы помещаете вычисляемый столбец с параметром PERSISTED в таблицу, в которой уже есть существующие данные (например, 'ABXY', 'X1', 'ETC'), которая вызывает исключение, когда применяется функция / выражение (например, приведение) к этому ваша СУБД не позволит вам сделать вычисляемый столбец. Если в вашей таблице нет данных, это позволит вам включить опцию PERSISTED, но впоследствии, когда вы попытаетесь вставить данные (например, insert into tbl(col) values('ABXY')), которые вызывают исключение, ваша СУБД не позволит вам сохранить ваши данные. Таким образом, в вашей таблице может быть сохранен только числовой текст, ваш вычисляемый столбец PERSISTED выродится в проверку ограничений, хотя и полную в обход.

Для справки вот пример сохраненного вычисляемого столбца:

ALTER TABLE tbl ADD
numeric_equivalent  AS cast(col as int) persisted

Теперь у некоторых из нас может возникнуть желание не включать опцию PERSISTED в вычисляемый столбец. Это было бы своего рода самоубийственным усилием с точки зрения производительности, потому что вы не сможете создать индекс для них позже. Когда позже вы захотите создать индекс для неискаженного вычисляемого столбца, а в таблице уже есть данные «ABXY», база данных не позволит вам создать индекс. Создание индекса должно получить значение из столбца, и если этот столбец вызывает исключение, он не позволит вам создать индекс для него.

Если мы попытаемся немного обмануть, т. Е. Мы сразу создадим индекс для этого неисчисляемого вычисляемого столбца при создании таблицы, база данных позволит вам это сделать. Но когда мы добавим «ABXY» в таблицу позже, она не будет сохранена, база данных автоматически строит индекс (ы) после того, как мы вставим данные в таблицу. Конструктор индекса получает исключение вместо данных, поэтому он не может сделать запись индекса для данных, которые мы пытались вставить, поэтому последующая вставка данных не произойдет.

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

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

Чтобы (просто перевести это в эквивалент Oracle):

create function cast_as_int(@n varchar(20)) returns int with schemabinding
begin

    begin try
       return cast(@n as int);
    end try
    begin catch 
       return null;
    end catch

end;

Обратите внимание, что перехват исключения в UDF будетеще не работает в Sql Server, но Microsoft планирует поддержать этот

Теперь это наш постоянный вычисляемый столбец без исключений:

ALTER TABLE tbl ADD
numeric_equivalent  AS cast_as_int(a) persisted

Удалитесуществующий индекс, а затем воссоздать его:

create index ix_num_equiv on tbl(numeric_equivalent)    

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

select * from tbl where numeric_equivalent = 0 and is_col_numeric = 1

Чтобы сделать его более производительным, поскольку столбец numeric_equivalent больше не вызывает исключений, мы больше не используем is_col_numeric, поэтому просто используйте это:

select * from tbl where numeric_equivalent = 0 
2 голосов
/ 22 апреля 2010

Нравится ли вам:

SELECT * FROM MY_TABLE 
WHERE REPLACE (MY_COLUMN, '0', NULL) IS NULL
AND MY_COLUMN IS NOT NULL;
0 голосов
/ 22 апреля 2010

Другая версия SQL будет:

...
where len(COLUMN_NAME) > 0
 and len(replace(COLUMN_NAME, '0', '')) = 0

То есть, где в столбце более 1 символа, и все они равны 0. Бросьте в TRIM, если могут быть начальные, конечные или встроенные пробелы.

0 голосов
/ 22 апреля 2010

Как насчет использования регулярных выражений (поддерживается oracle, я думаю, также MSSQL)

0 голосов
/ 22 апреля 2010

Опираясь на ответ КМ, вы можете сделать то же самое в Oracle, не создавая фактическую таблицу.

SELECT y.*
  FROM YourTable  y
  WHERE YourColumn IN
    (SELECT LPAD('0',level,'0') FROM dual CONNECT BY LEVEL <= 6)

или

SELECT y.*
  FROM YourTable  y
    INNER JOIN
     (SELECT LPAD('0',level,'0') zeros FROM dual CONNECT BY LEVEL <= 6) z
            ON y.YourColumn = z.zeros

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

0 голосов
/ 22 апреля 2010

Это также будет работать в Oracle (но не в SQL Server):

REPLACE(column_name, '0') IS NULL

Это будет работать в Oracle (и, возможно, также в SQL Server, вам придется проверить):

LTRIM(column_name, '0') IS NULL

В качестве альтернативы, поскольку это столбец VARCHAR (6), вы также можете просто проверить:

column_name IN ('0', '00', '000', '0000', '00000', '000000')

Это не красиво, но, вероятно, наиболее эффективно, если наколонка.

0 голосов
/ 22 апреля 2010

попробуйте это, которое должно быть в состоянии использовать и индексировать YourTable.COLUMN_NAME, если оно существует:

--SQL Server syntax, but should be similar in Oracle
--you could make this a temp of permanent table 
CREATE TABLE Zeros (Zero varchar(6))
INSERT INTO Zeros VALUES ('0')
INSERT INTO Zeros VALUES ('00')
INSERT INTO Zeros VALUES ('000')
INSERT INTO Zeros VALUES ('0000')
INSERT INTO Zeros VALUES ('00000')
INSERT INTO Zeros VALUES ('000000')

SELECT
    y.*
    FROM YourTable       y
        INNER JOIN Zeros z On y.COLUMN_NAME=z.Zero

EDIT
или даже просто так:

SELECT
    *
    FROM YourTable
    WHERE COLUMN_NAME IN ('0','00','000','0000','00000','000000')

здание от ответа Дэйва Косты:

Oracle:

SELECT
    *
    FROM YourTable
    WHERE YourColumn IN
        (SELECT LPAD('0',level,'0') FROM dual CONNECT BY LEVEL <= 6)

SQL Server 2005 и выше:

;WITH Zeros AS
(SELECT 
     CONVERT(varchar(6),'0') AS Zero
 UNION ALL
 SELECT '0'+CONVERT(varchar(5),Zero)
     FROM Zeros
     WHERE LEN(CONVERT(varchar(6),Zero))<6
)
select Zero from Zeros
SELECT
    y.*
    FROM YourTable       y
    WHERE y.COLUMN_NAME IN (SELECT Zero FROM Zeros)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...