2012-01-11 47 views
-1

我们是一家制造公司,我有一个SQL Table [FSDBGL],它包含我们每个项目的信息。这包括ItemNumber,ItemUPC和ItemStatus的列。 ItemUPC列中的一些数据对于所需的项目是空的。在SQL中生成/更新列中的唯一随机条形码编号

我需要做的是在ItemUPC列内分配/插入一个随机唯一的条形码编号(尚未被采用)。该号码需要为12位数字,并以“601040xxxxxx”开头,只能随机化最后6位数字。对于每个产品编号,这不必在每一行都完成。

- 检查/只更新[ItemNumber]的(40000-01之间 - 50000-01)(-01末也可能是-02)

我需要忽略/排除下面列获取大量属性:
- ItemStatus(仅当它设定在陈旧的“O”)
- ItemUPC(如果它已经有一个条形码)

我想自定义为此我可以填充单元格的SQL查询,并实现夜间进程以更新任何新创建的Item#。

下面是创建脚本视图:

USE [FSDBGL] 
GO 


SET ANSI_NULLS ON 
GO 

SET QUOTED_IDENTIFIER ON 
GO 

SET ANSI_PADDING ON 
GO 

CREATE TABLE [dbo].[Mfg_ITMMAST](
[IMPN] [varchar](30) NOT NULL, 
[IMDESC] [varchar](70) NOT NULL, 
[IMUPCCD] [varchar](13) NOT NULL, 
) ON [PRIMARY] 

GO 

SET ANSI_PADDING OFF 
GO 
+0

什么样的“SQL”? MSSQL,MySQL,PostgreSQL等? – 2012-01-11 21:14:58

+0

SQL Server 2010 – user988265 2012-01-11 21:16:37

+0

@ user988265 - 你的意思是SQL Server 2012,又名“Denali”? – Lamak 2012-01-11 21:35:29

回答

1

随机数字:您可以通过该网址使用了游标,并得到每一个使用ROUND(RAND() * 999999,0, 0)一个随机数,然后才进行碰撞缺少UPC每一行循环做更新。游标查询的where子句应该非常简单... ItemNumber,ItemStatus!='O',ItemUPC!= null或''或0(或任何默认值)的正则表达式。

该sproc应该可以在任何时间重新运行,因为它使用随机数并检查碰撞。

更有效的方法是使用序列号而不是随机数。只要您能够将最后一次使用的号码存储在某个表中,我相信您可以将一个查询添加所有UPC号码,而不必为每个号码使用UPDATE ... FROM语法和SELECT @counter = @counter + 1语法为用户变量运行多个号码。


编辑:添加存储过程和其他意见

让我首先要注意这个数据库的设计可能不是最优的。此表上没有主键,也没有索引。如果这个表有大量记录,查询速度会很慢,并且这个存储过程将会非常缓慢。我也不得不做一些假设。由于IMUPCCD不能为空,因此我假设当UPC为“空白”时,默认值为601040。由于没有主键,我无法通过游标进行更新,而是必须运行单独的更新语句,这也是较慢的。我还必须假设IMPN唯一标识了一行数据。我不确定这些假设是否正确,因此您可能需要修改sproc以适应您的情况。

此外,原始问题涉及ItemStatus,但架构中未给出状态列,因此我无法在测试中限制结果。但是,您可以轻松地将它添加到WHERE子句中的存储过程的DECLARE blanksCursor CURSOR FOR ... WHERE ...语句中。

测试数据(在数据库中调用计算器)

USE [stackoverflow] 
GO 
SET ANSI_NULLS ON 
GO 
SET QUOTED_IDENTIFIER ON 
GO 
SET ANSI_PADDING ON 
GO 
--DROP TABLE [dbo].[Mfg_ITMMAST]; 
CREATE TABLE [dbo].[Mfg_ITMMAST](
    [IMPN] [varchar](30) NOT NULL, 
    [IMDESC] [varchar](70) NOT NULL, 
    [IMUPCCD] [varchar](13) NOT NULL 
) ON [PRIMARY] 
GO 
SET ANSI_PADDING OFF 
GO 
INSERT [dbo].[Mfg_ITMMAST] ([IMPN], [IMDESC], [IMUPCCD]) VALUES (N'40000-01', N'test', N'601040') 
INSERT [dbo].[Mfg_ITMMAST] ([IMPN], [IMDESC], [IMUPCCD]) VALUES (N'41023-01', N'test', N'60104') 
INSERT [dbo].[Mfg_ITMMAST] ([IMPN], [IMDESC], [IMUPCCD]) VALUES (N'41001-02', N'test', N'601040') 
INSERT [dbo].[Mfg_ITMMAST] ([IMPN], [IMDESC], [IMUPCCD]) VALUES (N'51001-01', N'test', N'601040') 
INSERT [dbo].[Mfg_ITMMAST] ([IMPN], [IMDESC], [IMUPCCD]) VALUES (N'51001-02', N'test', N'601040') 
INSERT [dbo].[Mfg_ITMMAST] ([IMPN], [IMDESC], [IMUPCCD]) VALUES (N'51014-02', N'test', N'601040234567') 
INSERT [dbo].[Mfg_ITMMAST] ([IMPN], [IMDESC], [IMUPCCD]) VALUES (N'61001-01', N'test', N'601040') 

存储过程

CREATE PROCEDURE uspScanForBlankUpcs 
AS 

    -- setup variables for bringing in blank row data 
    DECLARE @IMPN [varchar](30), @IMUPCCD [varchar](13), 
      @blankUpc [varchar](13), @upcPrefix [varchar](6), 
      @random [varchar](6), @retryRandom bit; 
    SET @blankUpc = '601040'; -- This is the value of IMUPCCD when it is "blank" 
    SET @upcPrefix = '601040'; -- This is prefix for our randomly generated UPC 

    -- setup the cursor, query for items with "blank" UPCs 
    DECLARE blanksCursor CURSOR FOR 
    SELECT IMPN 
    FROM [Mfg_ITMMAST] 
    WHERE (LEFT(IMPN, 5) >= '40000' AND 
     LEFT(IMPN, 5) < '60000' AND 
     RIGHT(IMPN, 2) IN ('01','02')) AND 
     IMUPCCD = @blankUpc 
    ; 

    -- open the cursor 
    OPEN blanksCursor; 

    -- load the next row from the cursor 
    FETCH NEXT FROM blanksCursor 
    INTO @IMPN; 

    -- loop through each row of the cursor 
    WHILE @@FETCH_STATUS = 0 
    BEGIN 
    --PRINT 'IMPN: ' + @IMPN; 
    -- try to create a new random number 
    SET @retryRandom = 1; 
    WHILE @retryRandom = 1 
    BEGIN 
     -- get a random number for the UPC, then left-pad it with zeros to 6 digits 
     SET @random = RIGHT('00000' + CONVERT(VARCHAR, FLOOR(RAND() * 999999)), 6); 
     -- concatenate the UPC prefix with the random number 
     SET @IMUPCCD = @upcPrefix + @random 
     --PRINT 'IMUPCCD: ' + @IMUPCCD; 
     -- see if this UPC already exists on another item 
     IF (SELECT COUNT(*) FROM [Mfg_ITMMAST] WHERE [IMUPCCD] = @IMUPCCD) > 0 
     SET @retryRandom = 1; -- UPC already existed (collision) try again 
     ELSE 
     SET @retryRandom = 0; -- didn't already exist, so exit out of loop 
    END 

    --PRINT 'Updating...'; 
    -- Update the UPC with the random number 
    UPDATE [Mfg_ITMMAST] 
    SET IMUPCCD = @IMUPCCD 
    WHERE IMPN = @IMPN 
    ; 

    -- Load the next result 
    FETCH NEXT FROM blanksCursor 
    INTO @IMPN; 

    END 
    CLOSE blanksCursor; 
    DEALLOCATE blanksCursor; 

GO 

运行存储过程

exec uspScanForBlankUpcs; 

资源,我用了这个过程:
MSDN - Creating Stored Procedure
MSDN - DECLARE CURSOR (Transact-SQL)

+0

我对SQL管理/查询构建有点新,我理解你说的大部分内容。我已经阅读了帖子和问题,但是我仍然遇到了一些问题,要求通过WHERE子句来隔离我需要修改的SELECT语句,甚至还没有开始尝试找出数字的生成/检查。 – user988265 2012-01-11 22:56:38

+0

如果您发布数据库模式(至少是提到的部分),我可以更具体地说明您的存储过程应该是什么样子。右键单击表格,脚本到新查询窗口,然后编辑您的答案并复制并粘贴该数据。在它前面放置4个空格,以便语法突出显示。 – 2012-01-11 23:29:26

+0

选择[ItemNumber] ,[ItemDescription] ,[ItemUPC] FROM [FSDBGL] [DBO]。[_ NoLock_FS_Item] GO – user988265 2012-01-12 00:07:05

0

这个答案集中在如何从公司前缀的新产品分配UPC代码时,在有“缺口”序列或您正在重复使用来自不活动产品的UPC代码(不推荐)。

UPC代码由3部分组成:

第一个是公司前缀。 第二个是项目参考。
第三位是校验位。

在此示例中,公司前缀为0601040.前导零不在此问题的范围内。

项目引用是一个数字范围,其大小取决于公司前缀中的数字位数。在这个例子中,我们在公司前缀中有6位数字(不包括前导零)和1位数字,用于保留5位数字作为项目引用。这会使项目的参考范围为0到99999.您需要查找该范围内未使用的数字。

校验位使用UPC代码的其他11位计算: http://www.gs1.org/how-calculate-check-digit-manually

除非你需要存储无效的UPC代码,因为您从外部系统接受不良数据有优势,存储公司前缀和项目作为参考表中不同的领域,并使得UPC索引持久计算字段:

-- Function to output UPC code based on Company Prefix and Item Reference: 

CREATE FUNCTION [dbo].[calc_UPC] 
    (@company_prefix varchar(10), @item_reference int) 
RETURNS char(12) 
WITH SCHEMABINDING 
AS 
BEGIN 
declare 
    @upc char(12), 
    @checkdigit int 

if SUBSTRING(@company_prefix, 1, 1) = 0 
begin 

    set @upc = substring(@company_prefix, 2, 10) + right('000000000000' + ltrim(str(@item_reference)), 12 - len(@company_prefix)) 
    set @checkdigit = (1000 - (
     convert(int, substring(@upc, 1, 1)) * 3 + 
     convert(int, substring(@upc, 2, 1)) * 1 + 
     convert(int, substring(@upc, 3, 1)) * 3 + 
     convert(int, substring(@upc, 4, 1)) * 1 + 
     convert(int, substring(@upc, 5, 1)) * 3 + 
     convert(int, substring(@upc, 6, 1)) * 1 + 
     convert(int, substring(@upc, 7, 1)) * 3 + 
     convert(int, substring(@upc, 8, 1)) * 1 + 
     convert(int, substring(@upc, 9, 1)) * 3 + 
     convert(int, substring(@upc, 10, 1)) * 1 + 
     convert(int, substring(@upc, 11, 1)) * 3)) % 10 

    set @upc = rtrim(@upc) + ltrim(str(@checkdigit)) 
end 
return @upc 
END 
GO 

-- Example Table of products: 

CREATE TABLE [dbo].[Product](
    [product_id] [int] IDENTITY(1,1) NOT NULL, 
    [company_prefix] [varchar](10) NULL, 
    [item_reference] [int] NULL, 
    [upc] AS ([dbo].[calc_UPC]([company_prefix],[item_reference])) PERSISTED, 
CONSTRAINT [PK_Product] PRIMARY KEY CLUSTERED 
(
    [product_id] ASC 
)) 
ALTER TABLE [dbo].[Product] WITH CHECK ADD CONSTRAINT [item_reference_greater_equal_zero] CHECK (([item_reference]>=(0))) 
GO 
ALTER TABLE [dbo].[Product] CHECK CONSTRAINT [item_reference_greater_equal_zero] 
GO 

-- Existing records with UPC codes: 

insert product (company_prefix, item_reference) values ('0601040', 3) 
insert product (company_prefix, item_reference) values ('0601040', 5) 

-- Example of 4 new products without UPC codes 

insert product DEFAULT VALUES 
insert product DEFAULT VALUES 
insert product DEFAULT VALUES 
insert product DEFAULT VALUES 
GO 

-- Next we need a table of all possible item references. 
-- This is the best implementation I have found for generating numbers: 


--Creates a table of sequential numbers, useful for all sorts of things 
--Created 08/26/05 by Oskar Austegard from article at 
--http://msdn.microsoft.com/library/en-us/dnsqlpro03/html/sp03k1.asp 
--Limits: @Min and @Max must be between -2147483647 and 2147483647, including. 
--If @Max <= @Min, only a single record with @Min is created 
CREATE FUNCTION [dbo].[NumberTable] (@Min int, @Max int) 
RETURNS @T TABLE (Number int NOT NULL PRIMARY KEY) 
AS 
BEGIN 
    -- Seed the table with the min value 
    INSERT @T VALUES (@Min) 
    --Loop until all the rows are created, inserting ever more records for each iteration (1, 2, 4, etc) 
    WHILE @@ROWCOUNT > 0 
    BEGIN 
     INSERT @T 
     --Get the next values by adding the current max - start value + 1 to each existing number 
     --need to calculate increment value first to avoid arithmetic overflow near limits of int 
     SELECT t.Number + (x.MaxNumber - @Min + 1) 
     FROM @T t 
     CROSS JOIN (SELECT MaxNumber = MAX(Number) FROM @T) x --Current max 
     WHERE 
     --Do not exceed the Max - shift the increment to the right side to take advantage of index 
     t.Number <= @Max - (x.MaxNumber - @Min + 1) 
    END 
    RETURN 
END 

GO 

-- For 10,000 numbers the performance of this function is good, 
-- but when the range is known I prefer the performance I get with a static table: 
-- Create a table of numbers between 0 and 99999 
CREATE table Numbers (number int) 
insert Numbers (number) 
select n.Number 
from dbo.NumberTable(0, 99999) n 

-- Now we can easily assign UPC codes using the available item reference values in your Company Prefix in a single update: 

declare @company_prefix varchar(10) 
set @company_prefix = '0601040' -- The function requires the leading zero 
update 
    p 
set 
    item_reference = n.number, 
    company_prefix = @company_prefix 
from 
    (
     select 
      p.product_id, 
      ROW_NUMBER() OVER (order by product_id) [row] 
     from 
      dbo.product p 
     where 
      p.company_prefix is null 
    ) u 
    inner join dbo.product p on p.product_id = u.product_id 
    inner join 
    (
     select 
      s.Number, 
      ROW_NUMBER() over (order by s.Number) [row] 
     from 
      (
       select n.Number from  
       ( 
        select 
         n.Number 
        from 
         dbo.Numbers n --Table(@sequence, @size - 1) n 
         left outer join dbo.Product p 
          on p.company_prefix = @company_prefix 
           and n.Number = p.item_reference 
        where 
         p.product_id is null 
       ) n 
      ) s 
    ) n on n.[row] = u.[row] 
GO 
select * from product 

使用这种方法,你不必担心作废的支票数字,你可以轻松地分配UPC代码,以新产品从您的GS1分配的UCC块。这也使得从新的公司前缀开始分配UPC代码变得很容易。您可以用相同的方法支持EAN13代码,只需对函数进行小改动:

CREATE FUNCTION [dbo].[calc_EAN13] 
    (@company_prefix varchar(10), @item_reference int) 
RETURNS char(13) 
WITH SCHEMABINDING 
AS 
BEGIN 
declare 
    @ean13 char(13), 
    @checkdigit int 

set @ean13 = @company_prefix 
set @ean13 = @company_prefix + right('0000000000000' + ltrim(str(@item_reference)), 12 - len(@company_prefix)) 
set @checkdigit = (1000 - (
    convert(int, substring(@ean13, 1, 1)) * 1 + 
    convert(int, substring(@ean13, 2, 1)) * 3 + 
    convert(int, substring(@ean13, 3, 1)) * 1 + 
    convert(int, substring(@ean13, 4, 1)) * 3 + 
    convert(int, substring(@ean13, 5, 1)) * 1 + 
    convert(int, substring(@ean13, 6, 1)) * 3 + 
    convert(int, substring(@ean13, 7, 1)) * 1 + 
    convert(int, substring(@ean13, 8, 1)) * 3 + 
    convert(int, substring(@ean13, 9, 1)) * 1 + 
    convert(int, substring(@ean13, 10, 1)) * 3 + 
    convert(int, substring(@ean13, 11, 1)) * 1 + 
    convert(int, substring(@ean13, 12, 1)) * 3)) % 10 

set @ean13 = rtrim(@ean13) + ltrim(str(@checkdigit)) 
return @ean13 
END 
GO