2012-01-11 309 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