पता लगाएँ कि क्या NVARCHAR कॉलम में कोई मान वास्तव में यूनिकोड हैं


14

मुझे कुछ SQL सर्वर डेटाबेस विरासत में मिला है। SQL सर्वर 2014 मानक पर ETL'd हो जाता है जो एक स्रोत डेटाबेस से (मैं "Q" कॉल करूँगा) के बारे में 86.7 मिलियन पंक्तियों, और 41 कॉलम चौड़ा, के साथ एक तालिका है (मैं "G" कॉल करूंगा) लक्ष्य डेटाबेस (मैं "P" कहूंगा) SQL सर्वर 2008 R2 मानक पर समान तालिका नाम के साथ।

अर्थात [Q]। [G] ---> [P]। [G]

EDIT: 3/20/2017: कुछ लोगों ने पूछा है कि क्या स्रोत तालिका लक्ष्य तालिका का एकमात्र स्रोत है। हाँ, यह एकमात्र स्रोत है। जहाँ तक ETL जाता है, वहाँ कोई वास्तविक परिवर्तन नहीं हो रहा है; यह प्रभावी रूप से स्रोत डेटा की 1: 1 प्रति होने का इरादा है। इसलिए, इस लक्ष्य तालिका में अतिरिक्त स्रोत जोड़ने की कोई योजना नहीं है।

[Q]। [G] स्तंभों के आधे से थोड़ा अधिक वर्कर (स्रोत तालिका):

  • 13 कॉलम स्तंभ (80) हैं
  • स्तंभों में से 9 वर्कर (30) हैं
  • स्तंभों में से 2 वर्कर (8) हैं।

इसी प्रकार, [P] [G] में समान कॉलम NVARCHAR (लक्ष्य तालिका) हैं, जिसमें समान चौड़ाई वाले कॉलम # समान हैं। (दूसरे शब्दों में, समान लंबाई, लेकिन NVARCHAR)।

  • स्तंभों में से 13 NVARCHAR (80) हैं
  • स्तंभों में से 9 NVARCHAR (30) हैं
  • स्तंभों में से 2 NVARCHAR (8) हैं।

यह मेरा डिज़ाइन नहीं है।

मैं NVARCHAR से VARCHAR तक डेटा प्रकारों को [P]। मैं इसे सुरक्षित रूप से (रूपांतरण से डेटा हानि के बिना) करना चाहता हूं।

मैं इस बात की पुष्टि करने के लिए लक्ष्य तालिका में प्रत्येक NVARCHAR कॉलम में डेटा मान कैसे देख सकता हूं कि क्या स्तंभ में वास्तव में कोई यूनीकोड ​​डेटा है या नहीं?

एक क्वेरी (DMVs?) जो प्रत्येक NVARCHAR कॉलम के प्रत्येक मान (एक लूप में?) की जांच कर सकती है और मुझे बता सकती है कि क्या कोई मान वास्तविक यूनिकोड आदर्श समाधान होगा, लेकिन अन्य विधियों का स्वागत है।


2
सबसे पहले, अपनी प्रक्रिया पर विचार करें, और डेटा का उपयोग कैसे किया जाता है। में डेटा [G]ETLed है [P]। यदि [G]है varchar, और ETL प्रक्रिया एकमात्र तरीका है, जिसमें डेटा आता है [P], तब तक जब तक कि प्रक्रिया सही यूनिकोड वर्ण नहीं जोड़ती है, कोई भी नहीं होना चाहिए। यदि अन्य प्रक्रियाएं डेटा को जोड़ या संशोधित करती हैं [P], तो आपको अधिक सावधान रहने की आवश्यकता है - सिर्फ इसलिए कि सभी वर्तमान डेटा का varcharमतलब यह nvarcharनहीं हो सकता है कि डेटा कल नहीं जोड़ा जा सकता है। इसी तरह, यह संभव है कि जो कुछ भी डेटा का उपभोग कर रहा है उसे डेटा की [P]आवश्यकता हो nvarchar
RDFozz

जवाबों:


10

मान लीजिए कि आपके किसी कॉलम में कोई यूनिकोड डेटा नहीं है। यह सत्यापित करने के लिए कि आपको प्रत्येक पंक्ति के लिए स्तंभ मान पढ़ना होगा। जब तक आपके पास कॉलम पर एक इंडेक्स नहीं होता है, तब तक एक रोस्टोर टेबल के साथ आपको टेबल से हर डेटा पेज पढ़ना होगा। इस बात को ध्यान में रखते हुए कि मुझे लगता है कि मेज के खिलाफ एक एकल क्वेरी में सभी कॉलम चेक को संयोजित करने के लिए यह बहुत मायने रखता है। इस तरह आप कई बार तालिका के डेटा को नहीं पढ़ पाएंगे और आपको किसी कर्सर या किसी अन्य प्रकार के लूप को कोड नहीं करना पड़ेगा।

किसी एकल स्तंभ की जांच करने के लिए विश्वास करें कि आप ऐसा कर सकते हैं:

SELECT COLUMN_1
FROM [P].[Q]
WHERE CAST(COLUMN_1 AS VARCHAR(80)) <> CAST(COLUMN_1 AS NVARCHAR(80));

से एक डाली NVARCHARको VARCHARआप को छोड़कर अगर वहाँ यूनिकोड वर्ण हैं एक ही परिणाम देना चाहिए। यूनिकोड वर्णों में परिवर्तित किया जाएगा ?। इसलिए उपरोक्त कोड को NULLमामलों को सही ढंग से संभालना चाहिए । आपके पास जांचने के लिए 24 कॉलम हैं, इसलिए आप स्केलर एग्रीगेट्स का उपयोग करके एक ही क्वेरी में प्रत्येक कॉलम की जांच करते हैं। एक कार्यान्वयन नीचे है:

SELECT 
  MAX(CASE WHEN CAST(COLUMN_1 AS VARCHAR(80)) <> CAST(COLUMN_1 AS NVARCHAR(80)) THEN 1 ELSE 0 END) COLUMN_1_RESULT
...
, MAX(CASE WHEN CAST(COLUMN_14 AS VARCHAR(30)) <> CAST(COLUMN_14 AS NVARCHAR(30)) THEN 1 ELSE 0 END) COLUMN_14_RESULT
...
, MAX(CASE WHEN CAST(COLUMN_23 AS VARCHAR(8)) <> CAST(COLUMN_23 AS NVARCHAR(8)) THEN 1 ELSE 0 END) COLUMN_23_RESULT
FROM [P].[Q];

प्रत्येक कॉलम के लिए आपको इसका परिणाम मिलेगा 1यदि इसके किसी भी मान में यूनिकोड हो। का एक परिणाम 0का मतलब है कि सभी डेटा को सुरक्षित रूप से परिवर्तित किया जा सकता है।

मैं नए स्तंभ परिभाषाओं के साथ तालिका की प्रतिलिपि बनाने और वहां आपके डेटा की प्रतिलिपि बनाने की दृढ़ता से अनुशंसा करता हूं। आप महंगा रूपांतरण कर रहे हैं यदि आप इसे जगह में करते हैं तो एक प्रति बनाने से यह सब बहुत धीमा नहीं हो सकता है। प्रतिलिपि होने का अर्थ है कि आप आसानी से यह सत्यापित कर सकते हैं कि सभी डेटा अभी भी है (एक तरीका EXCEPT कीवर्ड का उपयोग करना है ) और आप ऑपरेशन को बहुत आसानी से पूर्ववत कर सकते हैं।

इसके अलावा, ध्यान रखें कि आपके पास वर्तमान में ऐसा कोई भी यूनिकोड डेटा नहीं हो सकता है जो भविष्य में ईटीएल यूनिकोड को पहले के साफ कॉलम में लोड कर सके। यदि आपकी ETL प्रक्रिया में इसके लिए कोई जांच नहीं है, तो आपको इस रूपांतरण को करने से पहले इसे जोड़ने पर विचार करना चाहिए।


जबकि @srutzky का उत्तर और चर्चा काफी अच्छी थी और इसमें सहायक जानकारी थी, जो मुझे मेरा प्रश्न पूछ रहा था, जो मुझे प्रदान किया गया था: यदि स्तंभों में किसी भी मूल्य में यूनिकोड है, तो मुझे बताने के लिए एक क्वेरी। इसलिए मैंने जो के उत्तर को स्वीकृत उत्तर के रूप में चिह्नित किया है। मैंने उन अन्य उत्तरों को वोट दिया, जिनसे मुझे भी मदद मिली।
जॉन जी होहेंगार्टन

@ जॉनगोहेनगार्टन और जो: यह ठीक है। मैंने क्वेरी का उल्लेख नहीं किया क्योंकि यह इस उत्तर के साथ-साथ स्कॉट की भी थी। मैं सिर्फ इतना कहूंगा कि NVARCHARकॉलम को बदलने की कोई आवश्यकता नहीं है NVARCHARक्योंकि यह पहले से ही टाइप है। और यह निश्चित नहीं है कि आपने अनजाने चरित्र को कैसे निर्धारित किया है, लेकिन आप VARBINARYUTF-16 बाइट दृश्यों को प्राप्त करने के लिए कॉलम को बदल सकते हैं । और UTF-16 रिवर्स बाइट क्रम है, इसलिए p= 0x7000और फिर आप कोड प्वाइंट प्राप्त करने के लिए उन दो बाइट्स को उल्टा करते हैं U+0070। लेकिन, अगर स्रोत VARCHAR है, तो यह एक यूनिकोड वर्ण नहीं हो सकता है। कुछ और चल रहा है। अधिक जानकारी चाहिए।
सोलोमन रटज़की

@srutzky मैंने डेटा प्रकार की पूर्ववर्ती समस्याओं से बचने के लिए कलाकारों को जोड़ा। आप सही हो सकते हैं कि इसकी आवश्यकता नहीं है। अन्य प्रश्न के लिए, मैंने UNICODE () और SUBSTRING () का सुझाव दिया। क्या वह काम करता है?
जो ओबिश

@ जॉनहॉन्गार्टन और जो: डेटा प्रकार की पूर्ववर्ती स्थिति को एक मुद्दा नहीं होना चाहिए जैसा कि संक्षेप में VARCHARरूपांतरित किया जाएगा NVARCHAR, लेकिन ऐसा करना बेहतर हो सकता है CONVERT(NVARCHAR(80), CONVERT(VARCHAR(80), column)) <> columnSUBSTRINGकभी-कभी काम करता है, लेकिन यह उन संपत्तियों के साथ काम नहीं करता है जब Collations का उपयोग करते हैं जो समाप्त नहीं होते हैं _SC, और एक जॉन का उपयोग नहीं करता है, हालांकि यहां एक समस्या नहीं है। लेकिन VARBINARY में कनवर्ट करना हमेशा काम करता है। और CONVERT(VARCHAR(10), CONVERT(NVARCHAR(10), '›'))परिणाम नहीं है ?, इसलिए मैं बाइट्स देखना चाहता हूं। ईटीएल प्रक्रिया ने इसे परिवर्तित कर दिया है।
सोलोमन रटज़की

5

कुछ भी करने से पहले, कृपया प्रश्न पर टिप्पणी में @RDFozz द्वारा प्रस्तुत प्रश्नों पर विचार करें:

  1. क्या इस तालिका को आबाद करने के अलावा कोई अन्य स्रोत हैं [Q].[G]?

    यदि प्रतिक्रिया "मैं 100% निश्चित हूं कि इस गंतव्य तालिका के लिए डेटा का एकमात्र स्रोत है" के बाहर कुछ भी है , तो कोई भी परिवर्तन न करें, भले ही वर्तमान में तालिका में डेटा को बिना परिवर्तित किया जा सके या नहीं। डेटा हानि।

  2. क्या निकट भविष्य में इस डेटा को आबाद करने के लिए अतिरिक्त स्रोतों को जोड़ने से संबंधित कोई योजना / चर्चा है?

    और मैं एक संबंधित प्रश्न जोड़ूंगा: क्या वर्तमान स्रोत तालिका (यानी [Q].[G]) में इसे परिवर्तित करके कई भाषाओं का समर्थन करने के आसपास कोई चर्चा हुई है NVARCHAR?

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

यहाँ मुख्य मुद्दा यूनिकोड कोड पॉइंट्स का इतना अधिक नहीं होना है जो (कभी भी) परिवर्तित नहीं हो सकता है , लेकिन अधिक कोड पॉइंट्स हैं जो सभी एक ही कोड पेज पर फिट नहीं होंगे। यूनिकोड के बारे में यह अच्छी बात है: यह सभी कोड पृष्ठों से वर्ण पकड़ सकता है। यदि आप इससे कनवर्ट करते हैं NVARCHAR- जहां आपको कोड पृष्ठों के बारे में चिंता करने की आवश्यकता नहीं है - VARCHARतो, आपको यह सुनिश्चित करने की आवश्यकता होगी कि गंतव्य कॉलम का Collation स्रोत कोड के समान कोड पेज का उपयोग कर रहा है। यह मान लेता है कि एक ही कोड पृष्ठ का उपयोग करके एक स्रोत या कई स्रोत हैं (आवश्यक रूप से समान Collation, हालांकि नहीं)। लेकिन यदि कई कोड पृष्ठ वाले कई स्रोत हैं, तो आप संभावित रूप से निम्नलिखित समस्या में भाग ले सकते हैं:

DECLARE @Reporting TABLE
(
  ID INT IDENTITY(1, 1) PRIMARY KEY,
  SourceSlovak VARCHAR(50) COLLATE Slovak_CI_AS,
  SourceHebrew VARCHAR(50) COLLATE Hebrew_CI_AS,
  Destination NVARCHAR(50) COLLATE Latin1_General_CI_AS,
  DestinationS VARCHAR(50) COLLATE Slovak_CI_AS,
  DestinationH VARCHAR(50) COLLATE Hebrew_CI_AS
);

INSERT INTO @Reporting ([SourceSlovak]) VALUES (0xDE20FA);
INSERT INTO @Reporting ([SourceHebrew]) VALUES (0xE820FA);

UPDATE @Reporting
SET    [Destination] = [SourceSlovak]
WHERE  [SourceSlovak] IS NOT NULL;

UPDATE @Reporting
SET    [Destination] = [SourceHebrew]
WHERE  [SourceHebrew] IS NOT NULL;

SELECT * FROM @Reporting;

UPDATE @Reporting
SET    [DestinationS] = [Destination],
       [DestinationH] = [Destination]

SELECT * FROM @Reporting;

रिटर्न (दूसरा परिणाम सेट):

ID    SourceSlovak    SourceHebrew    Destination    DestinationS    DestinationH
1     Ţ ú             NULL            Ţ ú            Ţ ú             ? ?
2     NULL            ט ת             ? ?            ט ת             ט ת

जैसा कि आप देख सकते हैं, उन सभी वर्णों को परिवर्तित कर सकते हैंVARCHAR , बस एक ही VARCHARकॉलम में नहीं ।

अपनी स्रोत तालिका के प्रत्येक स्तंभ के लिए कोड पृष्ठ क्या है, यह निर्धारित करने के लिए निम्नलिखित क्वेरी का उपयोग करें:

SELECT OBJECT_NAME(sc.[object_id]) AS [TableName],
       COLLATIONPROPERTY(sc.[collation_name], 'CodePage') AS [CodePage],
       sc.*
FROM   sys.columns sc
WHERE  OBJECT_NAME(sc.[object_id]) = N'source_table_name';

ऐसा कहे जाने के बाद....

आपने SQL Server 2008 R2, BUT पर होने का उल्लेख किया है, आपने यह नहीं कहा कि क्या संस्करण है। यदि आप एंटरप्राइज़ संस्करण पर होते हैं, तो इस सभी रूपांतरण सामग्री के बारे में भूल जाएं (क्योंकि आप संभवतः अंतरिक्ष को बचाने के लिए ऐसा कर रहे हैं), और डेटा संपीड़न सक्षम करें:

यूनिकोड संपीड़न कार्यान्वयन

यदि मानक संस्करण का उपयोग कर (और अब ऐसा लगता है कि आप then हैं) तो एक और loooong- शॉट संभावना है: SQL Server 2016 में अपग्रेड करें क्योंकि SP1 में डेटा संपीड़न का उपयोग करने के लिए सभी संस्करणों की क्षमता शामिल है (याद रखें, मैंने कहा था "लंबे समय तक शॉट "😉)।

बेशक, अब जब यह स्पष्ट कर दिया गया है कि डेटा के लिए केवल एक स्रोत है, तो आपको चिंता करने की कोई ज़रूरत नहीं है क्योंकि स्रोत में कोई यूनिकोड-केवल वर्ण या इसके विशिष्ट कोड के बाहर वर्ण नहीं हो सकते हैं। पृष्ठ। जिस स्थिति में, केवल एक चीज जिसे आपको ध्यान रखने की आवश्यकता है, वह स्रोत कॉलम के समान Collation का उपयोग कर रहा है, या कम से कम एक ही कोड पृष्ठ का उपयोग कर रहा है। मतलब, यदि स्रोत स्तंभ उपयोग कर रहा है SQL_Latin1_General_CP1_CI_AS, तो आप Latin1_General_100_CI_ASगंतव्य पर उपयोग कर सकते हैं।

एक बार जब आप जानते हैं कि क्या उपयोग करने के लिए Collation, आप या तो कर सकते हैं:

  • ALTER TABLE ... ALTER COLUMN ...होना VARCHAR(वर्तमान NULL/ NOT NULLसेटिंग निर्दिष्ट करना सुनिश्चित करें ), जिसके लिए 87 मिलियन पंक्तियों के लिए थोड़े समय और लेन-देन लॉग स्थान की आवश्यकता होती है।

  • प्रत्येक के लिए नए "ColumnName_tmp" कॉलम बनाएं और धीरे-धीरे UPDATEकरने के माध्यम से पॉप्युलेट करें TOP (1000) ... WHERE new_column IS NULL। एक बार जब सभी पंक्तियाँ पॉप्युलेट हो जाती हैं (और यह सत्यापित किया जाता है कि वे सभी सही ढंग से कॉपी किए गए हैं! तो आपको स्पष्ट लेनदेन में, UPDATEs को संभालने के लिए ट्रिगर की आवश्यकता हो सकती है), एक स्पष्ट लेनदेन में, sp_rename"वर्तमान" कॉलम के कॉलम नामों को स्वैप करने के लिए "होना" _Old "और फिर नए" _tmp "कॉलम को नामों से" _tmp "को हटाने के लिए। फिर sp_reconfigureटेबल पर संदर्भित किसी भी कैश्ड प्लान को अमान्य करने के लिए टेबल पर कॉल करें , और यदि कोई भी व्यू टेबल को संदर्भित करता है तो आपको कॉल करना होगा sp_refreshview(या ऐसा कुछ)। एक बार जब आप ऐप को वेरिफाई कर लेते हैं और ईटीएल इसके साथ सही तरीके से काम कर रहा है, तो आप कॉलम को छोड़ सकते हैं।


मैंने आपके द्वारा स्रोत और लक्ष्य दोनों पर प्रदान की गई CodePage क्वेरी चलाई, और कोडपेज 1252 है और BOTH स्रोत और लक्ष्य पर SQL_Latin1_General_CP1_CI_AS है।
जॉन जी होहेंगार्टन

@ जॉनहॉन्गार्टन मैं अभी नीचे फिर से अपडेट हुआ। आसान होने के लिए आप उसी Collation को रख सकते हैं, भले ही Latin1_General_100_CI_ASआप जो उपयोग कर रहे हैं , उससे बहुत बेहतर है। आसान अर्थ है कि छँटाई और तुलना व्यवहार उनके बीच एक ही होगा, भले ही नए Collation के रूप में अच्छा नहीं है जो मैंने अभी उल्लेख किया है।
सोलोमन रटज़की

4

जब मुझे असली नौकरी मिली तो मुझे इसके बारे में कुछ अनुभव था। चूंकि उस समय मैं आधार डेटा को संरक्षित करना चाहता था, और मुझे नए डेटा के लिए भी जिम्मेदार होना था जो संभवतः ऐसे चरित्र हो सकते थे जो फेरबदल में खो जाते थे, मैं एक गैर-संगणित गणना कॉलम के साथ गया था।

एसओ डेटा डंप से सुपर उपयोगकर्ता डेटाबेस की एक प्रति का उपयोग करके यहां एक त्वरित उदाहरण दिया गया है ।

हम बल्ले से सही देख सकते हैं कि यूनिकोड वर्ण के साथ DisplayNames हैं:

पागल

तो चलो एक गणना कॉलम जोड़कर यह पता करें कि कितने हैं! DisplayName स्तंभ है NVARCHAR(40)

USE SUPERUSER

ALTER TABLE dbo.Users
ADD DisplayNameStandard AS CONVERT(VARCHAR(40), DisplayName)

SELECT COUNT_BIG(*)
FROM dbo.Users AS u
WHERE u.DisplayName <> u.DisplayNameStandard

गिनती ~ 3000 पंक्तियों की ओर लौटती है

पागल

हालांकि, निष्पादन योजना थोड़ी सी खींची हुई है। क्वेरी तेजी से समाप्त होती है, लेकिन यह डेटा सेट बहुत बड़ा नहीं है।

पागल

चूंकि संकलित कॉलम को इंडेक्स जोड़ने के लिए बने रहने की आवश्यकता नहीं है, हम इनमें से एक कर सकते हैं:

CREATE UNIQUE NONCLUSTERED INDEX ix_helper
ON dbo.Users(DisplayName, DisplayNameStandard, Id)

जो हमें थोड़ा टिडियर प्लान देता है:

पागल

मैं समझता हूं कि यदि यह उत्तर नहीं है , क्योंकि इसमें वास्तु परिवर्तन शामिल हैं, लेकिन डेटा के आकार को देखते हुए, आप संभवतः प्रश्नों को सामना करने के लिए अनुक्रमणिका को जोड़कर देख रहे हैं जो कि वैसे भी तालिका में शामिल होते हैं।

उम्मीद है की यह मदद करेगा!


1

के उदाहरण का उपयोग कैसे करता है, तो एक क्षेत्र यूनिकोड डेटा होता है की जाँच करने के लिए, आप प्रत्येक स्तंभ में डेटा पढ़ सकते हैं और कर सकता है CASTऔर नीचे की जाँच करें:

--Test 1:
DECLARE @text NVARCHAR(100)
SET @text = N'This is non-Unicode text, in Unicode'
IF CAST(@text AS VARCHAR(MAX)) <> @text
PRINT 'Contains Unicode characters'
ELSE
PRINT 'No Unicode characters'
GO

--Test 2:
DECLARE @text NVARCHAR(100)
SET @text = N'This is Unicode (字) text, in Unicode'
IF CAST(@text AS VARCHAR(MAX)) <> @text
PRINT 'Contains Unicode characters'
ELSE
PRINT 'No Unicode characters'

GO
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.