我有一个varchar(5)字段。我需要在所有记录中找到第一个未使用的数字,以便它们以前导零开头。就像有一个00001和00003我想要查询返回00002.此外,许多记录包含字母,看起来像'G0542'。这些可以忽略不计。
我知道我很亲密。这似乎适用于SQL Server 2005,但不适用于2008或2012
http://sqlfiddle.com/#!3/4016a0/1
create table b_addr ( inst_no varchar(5) Unique );
insert into b_addr (inst_no) values ('00001');
insert into b_addr (inst_no) values ('00002');
insert into b_addr (inst_no) values ('00004');
--this is the problem line
insert into b_addr (inst_no) values ('A0045');
With usedNos as(
select CAST(b_addr.inst_no AS INT) as inst
from b_addr
where b_addr.inst_no LIKE '[0-9][0-9][0-9][0-9][0-9]')
SELECT
RIGHT('00000' + CONVERT(VARCHAR(5), COALESCE(min(inst)+1, 0)),5) AS next_inst_no
from usedNos where not exists (select null from usedNos usn where usn.inst = usedNos.inst +1)
我如何构建它以便它也可以在sql server 2008+中使用?
答案 0 :(得分:3)
以下是您的查询版本:
With usedNos as (
select CAST(case when isnumeric(b_addr.inst_no) = 1
then b_addr.inst_no
end AS INT) as inst
from b_addr
)
SELECT RIGHT('00000' + CONVERT(VARCHAR(5), COALESCE(min(inst)+1, 0)),5) AS next_inst_no
from usedNos
where inst is not null and
not exists (select 1
from usedNos usn
where usn.inst = usedNos.inst +1
);
关键是在isnumeric()
内使用case
。这可以保证除非值看起来像数字,否则不会尝试cast()
。如果它看起来不像数字,那么结果是NULL
,它会在外部where
子句中被过滤掉。
您的where
条款:
where b_addr.inst_no LIKE '[0-9][0-9][0-9][0-9][0-9]'
尝试做同样的事情。但是,SQL Server不保证在where
之前处理select
子句 - 这就是您收到意外错误的原因。
答案 1 :(得分:1)
您可以检查您尝试投射的号码是否为实数:
而不是
CAST(b_addr.inst_no AS INT) as inst
执行类似
的操作CAST(CASE WHEN ISNUMERIC(b_addr.inst_no) = 1 THEN b_addr.inst_no ELSE 0 END AS INT) as inst
这有点令人费解,但它确实有效。
答案 2 :(得分:1)
create table b_addr ( inst_no varchar(5) Unique );
insert into b_addr (inst_no) values ('00001');
insert into b_addr (inst_no) values ('00002');
insert into b_addr (inst_no) values ('00004');
--this is the problem line
insert into b_addr (inst_no) values ('A0045');
--Specify how many of the "next" unused numbers you want.
DECLARE @HowMany INT = 10
--Common Table Expression construct to generate sequential numbers.
;WITH
L0 AS(SELECT 1 AS c UNION ALL SELECT 1),
L1 AS(SELECT 1 AS c FROM L0 AS A, L0 AS B),
L2 AS(SELECT 1 AS c FROM L1 AS A, L1 AS B),
L3 AS(SELECT 1 AS c FROM L2 AS A, L2 AS B),
L4 AS(SELECT 1 AS c FROM L3 AS A, L3 AS B),
L5 AS(SELECT 1 AS c FROM L4 AS A, L4 AS B),
L6 AS(SELECT 1 AS c FROM L5 AS A, L5 AS B),
SequentialNumbers AS(SELECT ROW_NUMBER() OVER(ORDER BY c) AS Num FROM L6)
SELECT TOP(@HowMany) RIGHT('00000' + CAST(sn.Num AS VARCHAR), 5) AS NextNumbers
FROM SequentialNumbers sn
WHERE sn.Num NOT IN (
SELECT CAST(b_addr.inst_no AS INT) AS inst
FROM b_addr
WHERE b_addr.inst_no LIKE '[0-9][0-9][0-9][0-9][0-9]'
)
答案 3 :(得分:1)
这是另一个使用patindex
而不是喜欢的人。
http://sqlfiddle.com/#!6/1aee5/12
With usedNos as(
select b_addr.inst_no as inst
from b_addr
where patindex('[0-9][0-9][0-9][0-9][0-9]',b_addr.inst_no) > 0
)
SELECT
RIGHT('00000' + COALESCE(cast((min(cast(inst as int)) + 1) as varchar), '00000'),5) AS next_inst_no
from usedNos
where not exists (select null from usedNos usn where cast(usn.inst as int) = cast(usedNos.inst as int) + 1 )
--should return 00003, trying to find the first unused inst_no and padd it with zeros to make it 5 chars.