प्रारूप के साथ 'Id': YYYYNNNNNN प्रत्येक वर्ष NNNNNN भाग के साथ


11

मुझे व्यवसाय की आवश्यकता है कि इनवॉयस टेबल के प्रत्येक रिकॉर्ड में एक आईडी है जो YYYYNNNNNN जैसा दिखता है।

NNNNNN भाग को प्रत्येक वर्ष की शुरुआत में पुनः आरंभ करने की आवश्यकता है। तो 2016 में दर्ज पहली पंक्ति 2016000001 की तरह दिखेगी और दूसरी 2016000002 की तरह होगी। चलिए बताते हैं कि 2016 का आखिरी रिकॉर्ड 2016123456 था, अगली पंक्ति (2017 की) को 2017000001 की तरह दिखना चाहिए

मुझे प्राथमिक आईडी होने के लिए इस आईडी की आवश्यकता नहीं है और मैं निर्माण तिथि भी संग्रहीत करता हूं। विचार यह है कि यह 'डिस्प्ले आईडी' अद्वितीय है (इसलिए मैं इसके द्वारा क्वेरी कर सकता हूं) और मानव समूह-सक्षम, वर्ष तक।

यह संभावना नहीं है कि किसी भी रिकॉर्ड को हटा दिया जाएगा; हालाँकि, मुझे ऐसा कुछ करने के खिलाफ रक्षात्मक रूप से कोड करने की इच्छा होगी।

क्या कोई तरीका है कि मैं इस आईडी को अधिकतम आईडी के लिए इस बार हर बार एक नई पंक्ति डालने के बिना बना सकता हूं?

विचार:

  • CreateNewInvoiceSP, जो MAXउस वर्ष के लिए मूल्य प्राप्त करता है (yucky)
  • कुछ जादुई तरीके से ऐसा करने के लिए बनाया गया है (मैं सही सपना देख सकता हूं)
  • यूडीएफ IDENTITYया DEFAULTघोषणा में कुछ निर्दिष्ट करने में सक्षम होने के नाते (??)
  • एक दृश्य जो उपयोग करता है PARTITION OVER + ROW()(नष्ट हो जाएगा समस्याग्रस्त)
  • एक ट्रिगर पर INSERT(अभी भी कुछ MAXक्वेरी चलाने की आवश्यकता होगी :()
  • एक वार्षिक पृष्ठभूमि नौकरी, प्रत्येक वर्ष के लिए अधिकतम के साथ एक तालिका अपडेट की गई जो मैंने तब डाली ... कुछ ?!

जिनमें से सभी थोड़ा गैर आदर्श हैं। किसी भी विचार या भिन्नता का हालांकि स्वागत है!


आपके पास कुछ अच्छे उत्तर हैं लेकिन यदि आपके पास पीके के रूप में वर्ष है, तो अधिकतम का चयन करें बहुत तेज़ है।
पापाराज़ो

चुनिंदा अधिकतम आईडी क्वेरी का उपयोग करना एक आम बात है। उसका उपयोग करें।
उउर गुम्मशान

जवाबों:


17

आपके क्षेत्र में 2 तत्व हैं

  • साल
  • एक ऑटो इंक्रीमेंट नंबर

उन्हें एक क्षेत्र के रूप में संग्रहीत करने की आवश्यकता नहीं है

उदाहरण:

  • एक वर्ष का कॉलम जिसमें डिफ़ॉल्ट है YEAR(GETDATE())
  • एक क्रम पर आधारित एक संख्या स्तंभ।

फिर उन्हें (उपयुक्त स्वरूपण के साथ) एक संगणित कॉलम बनाएँ। अनुक्रम को वर्ष के परिवर्तन पर रीसेट किया जा सकता है।

SQLfiddle में नमूना कोड : * (SQLfield हमेशा काम नहीं करता है)

-- Create a sequence
CREATE SEQUENCE CountBy1
    START WITH 1
    INCREMENT BY 1 ;

-- Create a table
CREATE TABLE Orders
    (Yearly int NOT NULL DEFAULT (YEAR(GETDATE())),
    OrderID int NOT NULL DEFAULT (NEXT VALUE FOR CountBy1),
    Name varchar(20) NOT NULL,
    Qty int NOT NULL,
    -- computed column
    BusinessOrderID AS RIGHT('000' + CAST(Yearly AS VARCHAR(4)), 4)
                     + RIGHT('00000' + CAST(OrderID AS VARCHAR(6)), 6),
    PRIMARY KEY (Yearly, OrderID)
    ) ;


-- Insert two records for 2015
INSERT INTO Orders (Yearly, Name, Qty)
    VALUES
     (2015, 'Tire', 7),
     (2015, 'Seat', 8) ;


-- Restart the sequence (Add this also to an annual recurring 'Server Agent' Job)
ALTER SEQUENCE CountBy1
    RESTART WITH 1 ;

-- Insert three records, this year.
INSERT INTO Orders (Name, Qty)
    VALUES
     ('Tire', 2),
     ('Seat', 1),
     ('Brake', 1) ;

1
शायद यह प्रति वर्ष एक क्रम के लिए क्लीनर है। इस तरह से नियमित संचालन के हिस्से के रूप में डीडीएल निष्पादित करने की कोई आवश्यकता नहीं है।
यूएसआर

@ इसलिए मुझे SEQUENCE हर साल की शुरुआत में एक पृष्ठभूमि नौकरी की आवश्यकता होगी ?
डार्सीटॉमस

@usr दुख की बात है कि आप NEXT VALUE FORएक CASEबयान में उपयोग नहीं कर सकते (मैंने कोशिश की)
डार्सीटॉमस

8

क्या आपने बीज = 2016000000 के साथ एक पहचान क्षेत्र बनाने पर विचार किया?

 create table Table1 (
   id bigint identity(2016000000,1),
   field1 varchar(20)...
)

इस बीज को हर साल स्वत: अंकुश लगाया जाना चाहिए, उदाहरण के लिए 2017/1/1 की रात को आपको शेड्यूल करने की आवश्यकता है

DBCC CHECKIDENT (Table1, RESEED, 2017000000)

लेकिन मैं पहले से ही डिजाइन के साथ समस्याओं को देखता हूं, उदाहरण के लिए: यदि आपके पास मिलियन रिकॉर्ड हैं तो क्या होगा?


2
एक अन्य समस्या यह है कि रिकॉर्ड कालानुक्रमिक रूप से प्रकट नहीं होते हैं। अगर यह मामला है तो पहचान शायद जाने का रास्ता नहीं है।
डैनियल हट्मैचर

@LiyaTansky मेरे मामले में मुझे बताया गया है कि प्रति वर्ष केवल 50k रिकॉर्ड होना चाहिए। लेकिन मुझे लगता है कि आप इसके बारे में
1k

1

इस परिदृश्य में मैंने जो किया वह वर्ष को 10 ^ 6 से गुणा करना था और अनुक्रम मान को उसमें जोड़ना था। इसका लाभ यह है कि इसके (छोटे) चल रहे ओवरहेड के साथ एक संगणित क्षेत्र की आवश्यकता नहीं है और इस क्षेत्र का उपयोग एक के रूप में किया जा सकता है PRIMARY KEY

दो संभावित गोचा हैं:

  • सुनिश्चित करें कि आपका गुणक पर्याप्त रूप से बड़ा है ताकि कभी भी समाप्त न हो और

  • आपको अनुक्रम के कैशिंग के कारण अंतराल के बिना अनुक्रम की गारंटी नहीं है।

मैं SQL सर्वर का विशेषज्ञ नहीं हूं, लेकिन आप शायद अपने अनुक्रम को शून्य पर रीसेट करने के लिए 201x 00:00:00 पर ट्रिगर करने के लिए एक घटना सेट कर सकते हैं। यह भी है कि मैंने Firebird पर क्या किया (या यह इंटरबेस था?)।


1

संपादित करें: यह समाधान लोड के तहत काम नहीं करता है

मैं ट्रिगर्स का प्रशंसक नहीं हूं, लेकिन ऐसा लगता है कि मैं सबसे अच्छा काम कर सकता हूं।

पेशेवरों:

  • कोई बैकग्राउंड जॉब नहीं
  • DisplayId पर तेज़ क्वेरी कर सकते हैं
  • ट्रिगर को पिछले NNNNNN भाग के लिए स्कैन करने की आवश्यकता नहीं है
  • हर साल NNNNN भाग को पुनः आरंभ करेगा
  • प्रति वर्ष 100000 से अधिक पंक्तियों होने पर काम करेगा
  • भविष्य में काम करने के लिए स्कीमा अपडेट (जैसे, अनुक्रम रीसेट) की आवश्यकता नहीं है

संपादित करें: विपक्ष:

  • लोड के तहत असफल हो जाएगा (ड्राइंग बोर्ड पर वापस)

(क्रेडिट @ @ के लिए जैसा कि मैंने उनके उत्तर से कुछ प्रेरणा ली) (किसी भी फीड बैक और स्पष्ट गलतियों का स्वागत करते हुए :)

कुछ नए COLUMNs और a जोड़ेंINDEX

ALTER TABLE dbo.Invoices
ADD     [NNNNNNId]      INT  NULL 

ALTER TABLE dbo.Invoices
ADD [Year]              int NOT NULL DEFAULT (YEAR(GETDATE()))

ALTER TABLE dbo.Invoices
ADD [DisplayId]     AS  'INV' +
                        CAST([Year] AS VARCHAR(4))+
                        RIGHT('00000' + CAST([NNNNNNId] AS VARCHAR(4)),  IIF (5  >= LEN([NNNNNNId]), 5, LEN([NNNNNNId])) )                  

EXEC('CREATE NONCLUSTERED INDEX IX_Invoices_DisplayId
ON dbo.Invoices (DisplayId)')

नया जोड़ें TRIGGER

CREATE TRIGGER Invoices_DisplayId
ON dbo.Invoices
  AFTER  INSERT
AS 
BEGIN

SET NOCOUNT ON;    

UPDATE dbo.Invoices
SET NNNNNNId = CalcDisplayId
FROM (SELECT I.ID, IIF (Previous.Year = I.Year , (ISNULL(Previous.NNNNNNId,0) + 1), 1) AS CalcDisplayId  FROM
        (SELECT 
            ID  
           ,NNNNNNId 
           ,[year]
        FROM  dbo.Invoices
        ) AS Previous
    JOIN inserted AS I 
    ON Previous.Id = (I.Id -1) 
    ) X
WHERE 
   X.Id = dbo.Invoices.ID       
END
GO

मैं ऐसा न करने की सलाह देता हूं। लाइट लोड के तहत आने के बाद गतिरोध और इन्सर्ट फेल होने की संभावना है। क्या आपने एक डमी डेटाबेस में एक कॉपी डाल दी है और एक बार आवेषण करते समय कुछ दर्जन धागे के साथ इसे अंकित किया है (और शायद चयन / अद्यतन / हटाता है) यह देखने के लिए कि क्या होता है?
कोडी कोनिओर

@CodyKonior यह मूलभूत रूप से त्रुटिपूर्ण है या क्या इसे थोड़ा विवेकपूर्ण लॉकिंग के साथ पुनर्जीवित किया जा सकता है? यदि नहीं तो आप समस्या का सामना कैसे करेंगे?
डार्सीटॉमस

हममम। 10 धागे के साथ भाग गया। निश्चित नहीं है कि यह मृत ताले हैं, लेकिन मुझे कुछ दौड़ की स्थिति मिलती है। जहां एक ट्रिगर पूरा हो जाता है, पिछली पंक्तियों से पहले ट्रिगर समाप्त हो गया है। इससे NULLमूल्यों का एक समूह प्रविष्ट होता है। ड्रॉइंग बोर्ड पर वापस ...
डार्सीटॉमस

आपदा टल गई तो :-) मेरा रहस्य यह है कि मैंने लगभग पांच साल पहले जो कुछ किया था उसके पैटर्न को पहचान लिया। मुझे केवल यह पता है कि जिस तरह से आप ट्रिगर के अंदर टेबल को स्कैन करते हैं, अगले अनुक्रम की तलाश में चीजें लोड के तहत होती हैं। मुझे याद नहीं है कि मैंने इसे कैसे हल किया लेकिन मैं बाद में जांच कर सकता हूं।
कोडी कोनिओर

@CodyKonior मुझे नहीं लगता कि यह एक स्कैन ( ON Previous.Id = (I.Id -1) बस तलाश करना चाहिए) कर रहा है, लेकिन हाँ अभी भी काम नहीं करता है। अगर मैं डालने और ट्रिगर के दौरान तालिका (?) को लॉक कर सकता हूं तो मुझे लगता है कि यह काम करेगा। लेकिन यह एक कोड गंध के रूप में अच्छी तरह से लगता है।
डार्सीटॉमस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.