यह एक दिलचस्प मुद्दा है, तो चलो necromance है।
आइए विधि 1 की समस्याओं से शुरू करें:
समस्या: आप गति को बचाने के लिए अपवित्र कर रहे हैं।
SQL में (पोस्टस्टोर के साथ PostGreSQL को छोड़कर), आप एक पैरामीटर भाषा नहीं पारित कर सकते हैं, और कह सकते हैं:
SELECT ['DESCRIPTION_' + @in_language] FROM T_Products
तो आपको यह करना होगा:
SELECT
Product_UID
,
CASE @in_language
WHEN 'DE' THEN DESCRIPTION_DE
WHEN 'SP' THEN DESCRIPTION_SP
ELSE DESCRIPTION_EN
END AS Text
FROM T_Products
यदि आप एक नई भाषा जोड़ते हैं तो आपको अपने सभी प्रश्नों को बदलना होगा। यह स्वाभाविक रूप से "गतिशील एसक्यूएल" का उपयोग करता है, इसलिए आपको अपने सभी प्रश्नों को बदलने की आवश्यकता नहीं है।
यह आमतौर पर कुछ इस तरह से होता है (और इसका उपयोग विचारों या तालिका-मूल्यवान कार्यों में नहीं किया जा सकता है, जो वास्तव में एक समस्या है अगर आपको वास्तव में रिपोर्टिंग तिथि को फ़िल्टर करने की आवश्यकता है)
CREATE PROCEDURE [dbo].[sp_RPT_DATA_BadExample]
@in_mandant varchar(3)
,@in_language varchar(2)
,@in_building varchar(36)
,@in_wing varchar(36)
,@in_reportingdate varchar(50)
AS
BEGIN
DECLARE @sql varchar(MAX), @reportingdate datetime
-- Abrunden des Eingabedatums auf 00:00:00 Uhr
SET @reportingdate = CONVERT( datetime, @in_reportingdate)
SET @reportingdate = CAST(FLOOR(CAST(@reportingdate AS float)) AS datetime)
SET @in_reportingdate = CONVERT(varchar(50), @reportingdate)
SET NOCOUNT ON;
SET @sql='SELECT
Building_Nr AS RPT_Building_Number
,Building_Name AS RPT_Building_Name
,FloorType_Lang_' + @in_language + ' AS RPT_FloorType
,Wing_No AS RPT_Wing_Number
,Wing_Name AS RPT_Wing_Name
,Room_No AS RPT_Room_Number
,Room_Name AS RPT_Room_Name
FROM V_Whatever
WHERE SO_MDT_ID = ''' + @in_mandant + '''
AND
(
''' + @in_reportingdate + ''' BETWEEN CAST(FLOOR(CAST(Room_DateFrom AS float)) AS datetime) AND Room_DateTo
OR Room_DateFrom IS NULL
OR Room_DateTo IS NULL
)
'
IF @in_building <> '00000000-0000-0000-0000-000000000000' SET @sql=@sql + 'AND (Building_UID = ''' + @in_building + ''') '
IF @in_wing <> '00000000-0000-0000-0000-000000000000' SET @sql=@sql + 'AND (Wing_UID = ''' + @in_wing + ''') '
EXECUTE (@sql)
END
GO
इसके साथ समस्या
एक है) दिनांक-स्वरूपण बहुत भाषा-विशिष्ट है, इसलिए आपको वहां एक समस्या मिलती है, यदि आप आईएसओ प्रारूप में इनपुट नहीं करते हैं (जो औसत उद्यान-किस्म प्रोग्रामर आमतौर पर नहीं करता है, और मामले में एक रिपोर्ट जो उपयोगकर्ता को यकीन है कि नरक आपके लिए नहीं करेगी, भले ही स्पष्ट रूप से ऐसा करने का निर्देश दिया गया हो)।
और
बी) सबसे महत्वपूर्ण रूप से , आप किसी भी प्रकार की वाक्यविन्यास जाँच को ढीला करते हैं । यदि <insert name of your "favourite" person here>
स्कीमा को बदल देता है क्योंकि अचानक विंग की आवश्यकताएं बदल जाती हैं, और एक नया टेबल बनाया जाता है, तो पुराना एक छोड़ दिया जाता है, लेकिन संदर्भ क्षेत्र का नाम बदल दिया जाता है, आपको किसी भी प्रकार की चेतावनी नहीं मिलती है। एक रिपोर्ट तब भी काम करती है जब आप इसे विंग पैरामीटर (==> guide.empty) का चयन किए बिना चलाते हैं । लेकिन अचानक, जब एक वास्तविक उपयोगकर्ता वास्तव में एक विंग ==> का चयन करता है बूम का । यह विधि किसी भी प्रकार के परीक्षण को पूरी तरह से तोड़ देती है।
विधि 2:
संक्षेप में: "महान" विचार (चेतावनी - व्यंग्य), चलो विधि 3 के नुकसान (3 की धीमी गति जब कई प्रविष्टियाँ) विधि के बल्कि भयानक नुकसान के साथ गठबंधन करते हैं
। इस विधि का एकमात्र लाभ यह है कि आप रखते हैं एक तालिका में सभी अनुवाद, और इसलिए रखरखाव को सरल बनाते हैं। हालाँकि, एक ही चीज़ को विधि 1 और एक गतिशील SQL संग्रहीत कार्यविधि के साथ प्राप्त किया जा सकता है, और एक (संभवत: अस्थायी) तालिका जिसमें अनुवाद हैं, और लक्ष्य तालिका का नाम (और यह बहुत ही सरल है कि आपने अपने सभी पाठ-क्षेत्रों को नाम दिया है वही)।
विधि 3:
सभी अनुवादों के लिए एक तालिका: नुकसान: आपको एन फ़ील्ड के लिए जिन उत्पादों का आप अनुवाद करना चाहते हैं, उनके लिए n विदेशी कुंजी को स्टोर करना होगा। इसलिए, आपको एन फ़ील्ड के लिए एन जॉइन करना होगा। जब अनुवाद तालिका वैश्विक होती है, तो इसमें बहुत सी प्रविष्टियाँ होती हैं, और जोड़ धीमी हो जाती हैं। इसके अलावा, आपको हमेशा N_ फ़ील्ड के लिए T_TRANSLATION तालिका में शामिल होना होगा। यह काफी ओवरहेड है। अब, जब आप प्रति ग्राहक कस्टम अनुवाद समायोजित करना चाहते हैं तो आप क्या करते हैं? आपको अतिरिक्त तालिका पर एक और 2x n जुड़ना होगा। यदि आपको जुड़ना है, तो 2x2xn = 4n अतिरिक्त जोड़ के साथ 10 तालिकाओं को कहें, क्या गड़बड़ है! इसके अलावा, यह डिज़ाइन 2 टेबल के साथ एक ही अनुवाद का उपयोग करना संभव बनाता है। यदि मैं एक तालिका में आइटम का नाम बदलता हूं, तो क्या मैं वास्तव में किसी अन्य तालिका में प्रविष्टि को हर बार बदलना चाहता हूं?
इसके अलावा, आप तालिका को हटा नहीं सकते हैं और फिर से नहीं डाल सकते हैं, क्योंकि अब उत्पाद तालिका में विदेशी कुंजी हैं ... आप निश्चित रूप से FKs सेट <insert name of your "favourite" person here>
कर सकते हैं, और फिर तालिका हटा सकते हैं, और फिर से सम्मिलित कर सकते हैं सभी प्रविष्टियाँ newid () [या इन्सर्ट में id निर्दिष्ट करके, लेकिन आइडेंट-इन्सर्ट OFF ], और वह (और होगा) डेटा-कचरा (और अशक्त-संदर्भ अपवाद) को वास्तव में जल्द ही ले जाएगा।
विधि 4 (सूचीबद्ध नहीं): डेटाबेस में किसी XML फ़ील्ड में सभी भाषाओं को संग्रहीत करना। जैसे
-- CREATE TABLE MyTable(myfilename nvarchar(100) NULL, filemeta xml NULL )
;WITH CTE AS
(
-- INSERT INTO MyTable(myfilename, filemeta)
SELECT
'test.mp3' AS myfilename
--,CONVERT(XML, N'<?xml version="1.0" encoding="utf-16" standalone="yes"?><body>Hello</body>', 2)
--,CONVERT(XML, N'<?xml version="1.0" encoding="utf-16" standalone="yes"?><body><de>Hello</de></body>', 2)
,CONVERT(XML
, N'<?xml version="1.0" encoding="utf-16" standalone="yes"?>
<lang>
<de>Deutsch</de>
<fr>Français</fr>
<it>Ital&iano</it>
<en>English</en>
</lang>
'
, 2
) AS filemeta
)
SELECT
myfilename
,filemeta
--,filemeta.value('body', 'nvarchar')
--, filemeta.value('.', 'nvarchar(MAX)')
,filemeta.value('(/lang//de/node())[1]', 'nvarchar(MAX)') AS DE
,filemeta.value('(/lang//fr/node())[1]', 'nvarchar(MAX)') AS FR
,filemeta.value('(/lang//it/node())[1]', 'nvarchar(MAX)') AS IT
,filemeta.value('(/lang//en/node())[1]', 'nvarchar(MAX)') AS EN
FROM CTE
फिर आप SQL में XPath-Query द्वारा मूल्य प्राप्त कर सकते हैं, जहां आप स्ट्रिंग-चर को डाल सकते हैं
filemeta.value('(/lang//' + @in_language + '/node())[1]', 'nvarchar(MAX)') AS bla
और आप इस तरह से मूल्य को अपडेट कर सकते हैं:
UPDATE YOUR_TABLE
SET YOUR_XML_FIELD_NAME.modify('replace value of (/lang/de/text())[1] with ""I am a ''value ""')
WHERE id = 1
जहाँ आप के /lang/de/...
साथ बदल सकते हैं'.../' + @in_language + '/...'
PostGre hstore की तरह, सिवाय इसके कि XML को पार्स करने के ओवरहेड के कारण (PG hstore में एक साहचर्य सरणी से प्रविष्टि को पढ़ने के बजाय) यह बहुत धीमा हो जाता है और xml एन्कोडिंग उपयोगी होने के लिए बहुत दर्दनाक बनाता है।
विधि 5 (SunWuKung द्वारा अनुशंसित, जिसे आपको चुनना चाहिए): प्रत्येक "उत्पाद" तालिका के लिए एक अनुवाद तालिका। इसका मतलब है कि प्रति पंक्ति एक पंक्ति, और कई "टेक्स्ट" फ़ील्ड, इसलिए इसे एन फ़ील्ड पर केवल एक (बाएं) की आवश्यकता होती है। फिर आप आसानी से "उत्पाद" में एक डिफ़ॉल्ट-फ़ील्ड जोड़ सकते हैं -इस प्रकार, आप अनुवाद तालिका को आसानी से हटा सकते हैं और फिर से डाल सकते हैं, और आप कस्टम-अनुवाद (मांग पर) के लिए दूसरी तालिका बना सकते हैं, जिसे आप हटा भी सकते हैं और फिर से डालें), और आपके पास अभी भी सभी विदेशी कुंजी हैं।
आइए इस कार्य को देखने के लिए एक उदाहरण बनाएं:
सबसे पहले, टेबल बनाएं:
CREATE TABLE dbo.T_Languages
(
Lang_ID int NOT NULL
,Lang_NativeName national character varying(200) NULL
,Lang_EnglishName national character varying(200) NULL
,Lang_ISO_TwoLetterName character varying(10) NULL
,CONSTRAINT PK_T_Languages PRIMARY KEY ( Lang_ID )
);
GO
CREATE TABLE dbo.T_Products
(
PROD_Id int NOT NULL
,PROD_InternalName national character varying(255) NULL
,CONSTRAINT PK_T_Products PRIMARY KEY ( PROD_Id )
);
GO
CREATE TABLE dbo.T_Products_i18n
(
PROD_i18n_PROD_Id int NOT NULL
,PROD_i18n_Lang_Id int NOT NULL
,PROD_i18n_Text national character varying(200) NULL
,CONSTRAINT PK_T_Products_i18n PRIMARY KEY (PROD_i18n_PROD_Id, PROD_i18n_Lang_Id)
);
GO
-- ALTER TABLE dbo.T_Products_i18n WITH NOCHECK ADD CONSTRAINT FK_T_Products_i18n_T_Products FOREIGN KEY(PROD_i18n_PROD_Id)
ALTER TABLE dbo.T_Products_i18n
ADD CONSTRAINT FK_T_Products_i18n_T_Products
FOREIGN KEY(PROD_i18n_PROD_Id)
REFERENCES dbo.T_Products (PROD_Id)
ON DELETE CASCADE
GO
ALTER TABLE dbo.T_Products_i18n CHECK CONSTRAINT FK_T_Products_i18n_T_Products
GO
ALTER TABLE dbo.T_Products_i18n
ADD CONSTRAINT FK_T_Products_i18n_T_Languages
FOREIGN KEY( PROD_i18n_Lang_Id )
REFERENCES dbo.T_Languages( Lang_ID )
ON DELETE CASCADE
GO
ALTER TABLE dbo.T_Products_i18n CHECK CONSTRAINT FK_T_Products_i18n_T_Products
GO
CREATE TABLE dbo.T_Products_i18n_Cust
(
PROD_i18n_Cust_PROD_Id int NOT NULL
,PROD_i18n_Cust_Lang_Id int NOT NULL
,PROD_i18n_Cust_Text national character varying(200) NULL
,CONSTRAINT PK_T_Products_i18n_Cust PRIMARY KEY ( PROD_i18n_Cust_PROD_Id, PROD_i18n_Cust_Lang_Id )
);
GO
ALTER TABLE dbo.T_Products_i18n_Cust
ADD CONSTRAINT FK_T_Products_i18n_Cust_T_Languages
FOREIGN KEY(PROD_i18n_Cust_Lang_Id)
REFERENCES dbo.T_Languages (Lang_ID)
ALTER TABLE dbo.T_Products_i18n_Cust CHECK CONSTRAINT FK_T_Products_i18n_Cust_T_Languages
GO
ALTER TABLE dbo.T_Products_i18n_Cust
ADD CONSTRAINT FK_T_Products_i18n_Cust_T_Products
FOREIGN KEY(PROD_i18n_Cust_PROD_Id)
REFERENCES dbo.T_Products (PROD_Id)
GO
ALTER TABLE dbo.T_Products_i18n_Cust CHECK CONSTRAINT FK_T_Products_i18n_Cust_T_Products
GO
फिर डेटा भरें
DELETE FROM T_Languages;
INSERT INTO T_Languages (Lang_ID, Lang_NativeName, Lang_EnglishName, Lang_ISO_TwoLetterName) VALUES (1, N'English', N'English', N'EN');
INSERT INTO T_Languages (Lang_ID, Lang_NativeName, Lang_EnglishName, Lang_ISO_TwoLetterName) VALUES (2, N'Deutsch', N'German', N'DE');
INSERT INTO T_Languages (Lang_ID, Lang_NativeName, Lang_EnglishName, Lang_ISO_TwoLetterName) VALUES (3, N'Français', N'French', N'FR');
INSERT INTO T_Languages (Lang_ID, Lang_NativeName, Lang_EnglishName, Lang_ISO_TwoLetterName) VALUES (4, N'Italiano', N'Italian', N'IT');
INSERT INTO T_Languages (Lang_ID, Lang_NativeName, Lang_EnglishName, Lang_ISO_TwoLetterName) VALUES (5, N'Russki', N'Russian', N'RU');
INSERT INTO T_Languages (Lang_ID, Lang_NativeName, Lang_EnglishName, Lang_ISO_TwoLetterName) VALUES (6, N'Zhungwen', N'Chinese', N'ZH');
DELETE FROM T_Products;
INSERT INTO T_Products (PROD_Id, PROD_InternalName) VALUES (1, N'Orange Juice');
INSERT INTO T_Products (PROD_Id, PROD_InternalName) VALUES (2, N'Apple Juice');
INSERT INTO T_Products (PROD_Id, PROD_InternalName) VALUES (3, N'Banana Juice');
INSERT INTO T_Products (PROD_Id, PROD_InternalName) VALUES (4, N'Tomato Juice');
INSERT INTO T_Products (PROD_Id, PROD_InternalName) VALUES (5, N'Generic Fruit Juice');
DELETE FROM T_Products_i18n;
INSERT INTO T_Products_i18n (PROD_i18n_PROD_Id, PROD_i18n_Lang_Id, PROD_i18n_Text) VALUES (1, 1, N'Orange Juice');
INSERT INTO T_Products_i18n (PROD_i18n_PROD_Id, PROD_i18n_Lang_Id, PROD_i18n_Text) VALUES (1, 2, N'Orangensaft');
INSERT INTO T_Products_i18n (PROD_i18n_PROD_Id, PROD_i18n_Lang_Id, PROD_i18n_Text) VALUES (1, 3, N'Jus d''Orange');
INSERT INTO T_Products_i18n (PROD_i18n_PROD_Id, PROD_i18n_Lang_Id, PROD_i18n_Text) VALUES (1, 4, N'Succo d''arancia');
INSERT INTO T_Products_i18n (PROD_i18n_PROD_Id, PROD_i18n_Lang_Id, PROD_i18n_Text) VALUES (2, 1, N'Apple Juice');
INSERT INTO T_Products_i18n (PROD_i18n_PROD_Id, PROD_i18n_Lang_Id, PROD_i18n_Text) VALUES (2, 2, N'Apfelsaft');
DELETE FROM T_Products_i18n_Cust;
INSERT INTO T_Products_i18n_Cust (PROD_i18n_Cust_PROD_Id, PROD_i18n_Cust_Lang_Id, PROD_i18n_Cust_Text) VALUES (1, 2, N'Orangäsaft'); -- Swiss German, if you wonder
और फिर डेटा की क्वेरी करें:
DECLARE @__in_lang_id int
SET @__in_lang_id = (
SELECT Lang_ID
FROM T_Languages
WHERE Lang_ISO_TwoLetterName = 'DE'
)
SELECT
PROD_Id
,PROD_InternalName -- Default Fallback field (internal name/one language only setup), just in ResultSet for demo-purposes
,PROD_i18n_Text -- Translation text, just in ResultSet for demo-purposes
,PROD_i18n_Cust_Text -- Custom Translations (e.g. per customer) Just in ResultSet for demo-purposes
,COALESCE(PROD_i18n_Cust_Text, PROD_i18n_Text, PROD_InternalName) AS DisplayText -- What we actually want to show
FROM T_Products
LEFT JOIN T_Products_i18n
ON PROD_i18n_PROD_Id = T_Products.PROD_Id
AND PROD_i18n_Lang_Id = @__in_lang_id
LEFT JOIN T_Products_i18n_Cust
ON PROD_i18n_Cust_PROD_Id = T_Products.PROD_Id
AND PROD_i18n_Cust_Lang_Id = @__in_lang_id
यदि आप आलसी हैं, तो आप भाषा तालिका के प्राथमिक-कुंजी के रूप में ISO-TwoLetterName ('DE', 'EN' इत्यादि) का भी उपयोग कर सकते हैं, तो आपको भाषा आईडी देखने की आवश्यकता नहीं है। लेकिन यदि आप ऐसा करते हैं, तो आप शायद इसके बजाय IETF- भाषा टैग का उपयोग करना चाहते हैं , जो बेहतर है, क्योंकि आपको डी-सीएच और डी-डीई मिलता है, जो वास्तव में एक ही ऑर्टोग्राफी-वार नहीं है (हर जगह of के बजाय डबल एस) , हालांकि यह वही आधार-भाषा है। यह सिर्फ एक छोटा सा विवरण है जो आपके लिए महत्वपूर्ण हो सकता है, विशेष रूप से यह देखते हुए कि en-US और en-GB / en-CA / en-AU या fr-FR / fr-CA में समान मुद्दे हैं।
Quote: हमें इसकी आवश्यकता नहीं है, हम केवल अपना सॉफ्टवेयर अंग्रेजी में करते हैं।
उत्तर: हां - लेकिन कौन सा ??
वैसे भी, यदि आप एक पूर्णांक आईडी का उपयोग करते हैं, तो आप लचीले हैं, और किसी भी समय अपना तरीका बदल सकते हैं।
और आपको उस पूर्णांक का उपयोग करना चाहिए, क्योंकि एक बॉटेड डीबी डिज़ाइन की तुलना में अधिक कष्टप्रद, विनाशकारी और परेशानी नहीं है।
RFC 5646 , ISO 639-2 , भी देखें
और, यदि आप अभी भी "हम" केवल "केवल एक संस्कृति" के लिए अपना आवेदन करते हैं (जैसे एन-यूएस आमतौर पर) - इसलिए मुझे उस अतिरिक्त पूर्णांक की आवश्यकता नहीं है, यह एक अच्छा समय और जगह का उल्लेख करने के लिए होगा Iana भाषा टैग , यह नहीं होगा?
क्योंकि वे इस तरह से जाते हैं:
de-DE-1901
de-DE-1996
तथा
de-CH-1901
de-CH-1996
(1996 में एक ऑर्थोग्राफी सुधार था ...) एक शब्द को शब्दकोष में खोजने की कोशिश करें अगर वह गलत है; यह कानूनी और सार्वजनिक सेवा पोर्टलों से निपटने वाले अनुप्रयोगों में बहुत महत्वपूर्ण हो जाता है।
इससे भी महत्वपूर्ण बात यह है कि ऐसे क्षेत्र हैं जो सिरिलिक से लैटिन वर्णमाला में बदल रहे हैं, जो कुछ अस्पष्ट ऑर्थोग्राफी सुधार के सतही उपद्रव की तुलना में अधिक परेशानी भरा हो सकता है, यही कारण है कि यह एक महत्वपूर्ण विचार भी हो सकता है, जिसके आधार पर आप किस देश में रहते हैं। एक तरह से या दूसरे, यह बेहतर है कि वहाँ पूर्णांक है, बस के मामले में ...
संपादित करें:
और ON DELETE CASCADE
बाद में जोड़कर
REFERENCES dbo.T_Products( PROD_Id )
आप बस कह सकते हैं: DELETE FROM T_Products
और कोई विदेशी कुंजी उल्लंघन न करें।
टक्कर के लिए, मैं इसे इस तरह से करूँगा:
ए) अपने खुद के दाल
बी) भाषा तालिका में वांछित कोलाज़ नाम सहेजें
आप कोलाज को अपनी तालिका में रखना चाहते हैं, उदाहरण के लिए:
SELECT * FROM sys.fn_helpcollations()
WHERE description LIKE '%insensitive%'
AND name LIKE '%german%'
ग) क्या आपके पास स्थितीकरण नाम उपलब्ध है
डी) अपनी एसक्यूएल को इस तरह लिखें:
SELECT
COALESCE(GRP_Name_i18n_cust, GRP_Name_i18n, GRP_Name) AS GroupName
FROM T_Groups
ORDER BY GroupName COLLATE {#COLLATION}
ई) तब, आप इसे अपने DAL में कर सकते हैं:
cmd.CommandText = cmd.CommandText.Replace("{#COLLATION}", auth.user.language.collation)
जो तब आपको यह पूरी तरह से रचित SQL-Query देगा
SELECT
COALESCE(GRP_Name_i18n_cust, GRP_Name_i18n, GRP_Name) AS GroupName
FROM T_Groups
ORDER BY GroupName COLLATE German_PhoneBook_CI_AI