获取列中仅包含0的表中的所有行

时间:2010-04-22 13:18:22

标签: sql sql-server oracle

我遇到了一个小问题

我需要一个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 个答案:

答案 0 :(得分:3)

这是最简单的一个:

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

如果你不为该表达式创建计算列,你可以选择基于函数的索引(注意:Oracle和Postgres已经支持这个;从2008版开始的Sql Server,还没有)来提高性能:

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

<强> [编辑]

我只是为了后代保留下面的答案,我试图解释如何使查询使用计算列的索引。

使用此:

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

对于Oracle上的ISNUMERIC需求,请使用:http://www.oracle.com/technology/oramag/oracle/04-jul/o44asktom.html

<强> [编辑]

@Charles,re:Oracle上的计算列:

对于支持计算列的RDBMS但它没有持久化选项,是的,它会为每一行调用函数。如果它支持持久化列,它将不会进行函数调用,您在该表上具有从该函数预先计算的实列。现在,如果数据可能使函数引发异常,则有两种情况。

首先,如果你没有指定persist,它将允许你保存计算列(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支持short-circuiting):

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'),当应用函数/表达式(例如强制转换)时引发异常对它来说,你的RDBMS不允许你制作一个计算列。如果您的表没有数据,它将允许您放置PERSISTED选项,但之后当您尝试插入引发异常的数据(例如insert into tbl(col) values('ABXY'))时,您的RDBMS将不允许您保存数据。因此,只有数字文本可以保存在您的表中,您的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 have plans to support that

这是我们的非异常提升持续计算列:

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 

答案 1 :(得分:2)

你喜欢:

SELECT * FROM MY_TABLE 
WHERE REPLACE (MY_COLUMN, '0', NULL) IS NULL
AND MY_COLUMN IS NOT NULL;

答案 2 :(得分:0)

尝试这个,如果它存在,它应该能够在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

修改
甚至只是这个:

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

建立Dave Costa的答案:

甲骨文:

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)

答案 3 :(得分:0)

这也适用于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')

这不是很好,但如果列上有索引,它可能是最有效的。

答案 4 :(得分:0)

建立KM的答案,您可以在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更改为新的长度。

答案 5 :(得分:0)

如何使用正则表达式(由oracle支持,我认为也是MSSQL)

答案 6 :(得分:0)

另一个SQL版本是:

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

,即列中有超过1个字符,并且所有字符都为0.如果可以有前导,尾随或嵌入空格,则在TRIM中抛出。