JSON से SQL: INSERT कथन, तालिका डिज़ाइन और प्रकार मैपिंग के जाल
मुख्य बात: JSON को SQL INSERT में बदलने में nested तालिका विभाजन, NULL बनाम अनुपस्थित, escaping और बोली के अंतर छिपे हैं। हर जाल के लिए चलाने योग्य कथन और तुलना तालिकाएँ।

सार: तालिकाएँ डिज़ाइन करने से पहले तय करें कि एक पंक्ति किस व्यवसाय इकाई को दर्शाती है, फिर एक ही transaction के भीतर स्पष्ट बहु-पंक्ति VALUES लिखें; फ़ील्ड नामकरण और प्रकार मैपिंग आयात से पहले तय करें, उन्हें SQL में पैच न करें।
JSON बनावट बताता है; SQL बाधाएँ भी बताता है। जब आप पहले को दूसरे में ले जाते हैं, तो जो भी बाधा आपने नहीं लिखी, वह बाद में गंदे डेटा के रूप में लौटती है।
1. पहले तय करें: एक तालिका या JSON की एक पंक्ति पर एक रिकॉर्ड
कोई तालिका डिज़ाइन करने से पहले डेटा की सबसे बाहरी परत देखें। यह निर्णय गलत हुआ तो उसके बाद का हर INSERT दोबारा लिखना पड़ेगा।
| JSON संरचना | सुझाया तालिका डिज़ाइन | मुख्य जोखिम |
|---|---|---|
Object का सपाट array [{...},{...}] |
एक तालिका, कई पंक्तियाँ | रिकॉर्ड्स में असंगत key से union के ज़्यादातर स्तंभ NULL रह जाते हैं |
Array को लिपेटता object {"data":[...],"total":100} |
एक तालिका, केवल data लें |
total को हर पंक्ति में लिखने से फ़ील्ड का अर्थ गड़बड़ा जाता है |
Nested object {"user":{"name":"x"}} |
मूल तालिका और flatten किए स्तंभ, या मूल और संबंधित तालिका | अनियंत्रित विस्तार, दोहराए स्तंभ नाम |
Array फ़ील्ड {"tags":["a","b"]} |
बाल तालिका, या JSON/TEXT स्तंभ | one-to-many संबंध एक सेल में ठूँसा गया और tag से क्वेरी नहीं हो सकती |
| असंगत key | Union स्तंभ, या प्रकार से तालिकाएँ बाँटें | "key अनुपस्थित" और "मान null" एक ही NULL में सिमट जाते हैं |
केवल एक मानदंड है: एक पंक्ति किस व्यवसाय इकाई को दर्शाती है? यदि पंक्ति एक ऑर्डर है, तो पंक्ति-आइटम अपनी अलग तालिका में रहने चाहिए। यदि पंक्ति एक उपयोगकर्ता है, तो tag बाल तालिका या JSON स्तंभ में जाते हैं, comma से जुड़ी स्ट्रिंग में नहीं।
2. Array से INSERT तक: सबसे छोटा चलाने योग्य टेम्पलेट
सबसे सुरक्षित रूप हमेशा स्तंभ नाम स्पष्ट रूप से सूचीबद्ध करता है और स्तंभ क्रम पर कभी निर्भर नहीं करता:
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE
);
INSERT INTO users (id, name, email) VALUES
(1, 'Ada', 'ada@example.com'),
(2, 'Linus', NULL);
तीन विवरण मायने रखते हैं:
- हर स्तंभ नाम लिखें। जब schema में बाद में कोई स्तंभ जुड़े, तो कुछ भी गलत फ़ील्ड में नहीं खिसकेगा।
- बहु-पंक्ति
VALUESSQLite, PostgreSQL और MySQL में चलता है, और single-row insert के लूप की तुलना में बहुत सारे round trip बचाता है। NULLएक keyword है; उसे उद्धरण में न रखें।'NULL'लिखने से चार अक्षरों की स्ट्रिंग संग्रहीत होती है।
यदि JSON की key स्तंभ नामों से मेल न खाएँ (userId बनाम user_id), तो मैपिंग परत जोड़ें। SQL के भीतर COALESCE से अनुमान न लगाएँ। डेटा परत में फ़ील्ड नाम ठीक करना क्वेरी में पैच करने से कहीं सस्ता है।
3. Nested object: मूल तालिका और बाल तालिका
ऑर्डर इस one-to-many का उत्कृष्ट उदाहरण हैं:
INSERT INTO orders (id, customer_name, total_cents) VALUES
(1001, 'Ada', 12900);
INSERT INTO order_items (order_id, sku, quantity) VALUES
(1001, 'A-1', 2),
(1001, 'B-7', 1);
यह JSON {"id":1001,"customer":"Ada","total":129.00,"items":[{"sku":"A-1","quantity":2},{"sku":"B-7","quantity":1}]} से मेल खाता है।
मुख्य बातें:
itemsarray की लंबाई बाल पंक्तियों की संख्या तय करती है; मूल तालिका में ठीक एक पंक्ति जाती है।- धनराशि पूर्णांक सेंट (
total_cents) में रखें, कभीFLOATमें नहीं। - बाल तालिका को
order_idforeign key दें और दोनों insert एक ही transaction में commit करें, वरना मूल पंक्ति मौजूद रह सकती है जबकि उसके आइटम गायब हों। - यदि डेटा केवल पढ़ा जाता है और कोई पंक्ति-आइटम से filter नहीं करता, तो JSON स्तंभ स्वीकार्य है (PostgreSQL
jsonb, MySQLJSON, या json1 फ़ंक्शन के साथ SQLiteTEXT), पर कीमत स्वीकार करें: कोई indexing नहीं और क्वेरी अटपटी।
user.address जैसे स्थिर nested object को स्तंभों में flatten करें (address_city, address_zip)। तालिका में बाँटें केवल तब जब nesting बढ़ती रह सकती है; स्थिर आकार बाँटने से बस JOIN जुड़ जाते हैं।
4. NULL बनाम अनुपस्थित फ़ील्ड: SQL में "अनुपस्थित key" नहीं होती
JSON key को पूरी तरह अनुपस्थित होने देता है। SQL के स्तंभ स्थिर हैं। आयात के बाद दोनों मामले एक जैसे दिखते हैं — NULL — भले उनके अर्थ अलग हो सकते हैं:
| JSON स्थिति | डेटाबेस क्या दिखाता है | सुझाया समाधान |
|---|---|---|
| Key अनुपस्थित है | NULL |
तालिका परिभाषा में डिफ़ॉल्ट जोड़ें, या _present मार्कर स्तंभ |
Key मौजूद है और उसका मान null है |
NULL |
अर्थ है उपयोगकर्ता ने मान स्पष्ट रूप से खाली किया |
| Key मौजूद है और खाली स्ट्रिंग है | '' |
NULL से अलग पहचाना जा सकता, पर क्वेरी में भ्रम आसान |
| संख्या फ़ील्ड अनुपस्थित | NULL |
कभी 0 को "अनुपस्थित" का रूप न बनने दें |
दो आम गलतियाँ:
- NULL के बजाय खाली स्ट्रिंग इस्तेमाल करना। बाद में
WHERE email IS NULLउन पंक्तियों को छोड़ देता है, और आपका "बिना ईमेल वाले उपयोगकर्ता" मापन चुपचाप कम गिनता है। col = NULLसे परीक्षण करना। SQL के तीन-मान तर्क मेंNULL = NULLका मूल्यांकनNULLहोता है,TRUEनहीं। आपकोIS NULLलिखना ही होगा:
SELECT COUNT(*) FROM users WHERE email IS NULL;
यदि आपको यह बचाना है कि कोई key मौजूद थी या नहीं, तो NULL से अनुमान लगाने के बजाय boolean मार्कर स्तंभ जोड़ें। API अनुबंध में भी यह बताया जाना चाहिए; देखें JSON API डिज़ाइन और फ़ील्ड नामकरण।
5. Quoting और escaping: SQL स्ट्रिंग कभी न जोड़ें
JSON स्ट्रिंग में single quote, double quote, backslash, newline और यहाँ तक कि \u0000 हो सकता है। हाथ से बनी स्ट्रिंग में एक छूटा escape बाकी कथन को खिसका देता है, और सबसे बुरी स्थिति में SQL injection बन जाता है।
मानक SQL में single quote को दोहराकर escape किया जाता है:
INSERT INTO notes (id, content) VALUES (1, 'It''s a ''quoted'' value');
उत्पादन कोड को parameter binding इस्तेमाल करनी चाहिए और escaping तथा प्रकार driver को संभालने देना चाहिए:
// PostgreSQL uses $1/$2, MySQL uses ?; this example uses node-postgres
await client.query('INSERT INTO users (id, name) VALUES ($1, $2)', [row.id, row.name]);
हाथ से escape केवल तब करें जब .sql फ़ाइल निर्यात करनी ही हो, और तब कम से कम:
'को''से बदलें;- यदि newline बचाए जा रहे हैं, तो पुष्टि करें कि लक्ष्य डेटाबेस बहु-पंक्ति स्ट्रिंग शाब्दिक रूप स्वीकार करता है;
\u0000हटाएँ, क्योंकि अधिकांश डेटाबेस स्ट्रिंग के भीतर NUL अस्वीकार करते हैं;- MySQL डिफ़ॉल्ट रूप से backslash को escape अक्षर मानता है, इसलिए
\को\\बनना होगा, या standard mode अपनाएँ।
JSON के double quote को SQL की single-quoted स्ट्रिंग के भीतर escape करने की ज़रूरत नहीं। Escaping की अतिरिक्त परत जोड़ना ही है जिससे आपके डेटा में भटके हुए backslash आ जाते हैं।
6. प्रकार मैपिंग: छह JSON मान SQL में कहाँ जाते हैं
JSON में केवल string, number, boolean, null, object और array हैं। SQL की प्रकार प्रणाली कहीं बारीक है। गलतियाँ आमतौर पर व्याकरण के बजाय परिशुद्धता और शब्दार्थ में होती हैं:
| JSON मान | सुझाया SQL प्रकार | कहाँ टूटता है |
|---|---|---|
| पूर्णांक | INTEGER / BIGINT |
2^53 से ऊपर JavaScript पक्ष पहले ही परिशुद्धता खो चुका है, इसलिए संग्रहीत मान भी गलत है |
| दशमलव, धनराशि | NUMERIC / DECIMAL |
FLOAT बाइनरी फ़्लोटिंग पॉइंट है; 0.1 + 0.2 सटीक नहीं |
| स्ट्रिंग | TEXT / VARCHAR(n) |
लंबाई सीमाएँ काटती हैं; collation तुलनाएँ बदलती है |
| Boolean | BOOLEAN / TINYINT(1) |
SQLite में boolean प्रकार नहीं, 0/1 संग्रहीत करता है |
| null | NULL |
अनुपस्थित फ़ील्ड से अलग नहीं पहचाना जा सकता |
| तारीख स्ट्रिंग | DATE / TIMESTAMP / TIMESTAMPTZ |
समय क्षेत्र खो जाता है, मिलीसेकंड कटते हैं, अवैध प्रारूप |
| Enum स्ट्रिंग | CHECK के साथ TEXT, या enum तालिका |
केस का अंतर और गंदे मानों पर कोई बाधा नहीं |
| Object / array | बाल तालिका, या JSON / JSONB |
स्ट्रिंग के रूप में संग्रहीत होने पर index या क्वेरी नहीं हो सकती |
Boolean सबसे सूक्ष्म bug छिपाता है: JSON false MySQL TINYINT(1) में 0 बन जाता है, round-trip ठीक दिखता है, और कोई ध्यान नहीं देता। पर जिस पल कोई कोड स्ट्रिंग "false" पर if (value) करता है, वह truthy माना जाता है। रूपांतरण स्क्रिप्ट को स्पष्ट रूप से Boolean(value) कॉल करना चाहिए।
PostgreSQL JSONB key को पार्स और डीडुप्लिकेट करता है, अंतिम बार आने वाला रखता है, जो क्वेरी के लिए उपयुक्त है; JSON मूल पाठ और key क्रम सुरक्षित रखता है। SQLite JSON बस TEXT और कुछ फ़ंक्शन है, कोई वास्तविक प्रकार बाधा नहीं। चुनने से पहले पूछें कि क्या इस तालिका को JSON के भीतर के फ़ील्ड पर filter करना है।
7. Bulk insert और transaction: batch, commit, दोबारा चलाने योग्य बनाएँ
मात्रा बढ़ने पर तीन चीज़ें तय करती हैं कि आयात नियंत्रण में रहे:
- Batch। N single-row
INSERTकथनों का लूप N नेटवर्क round trips की कीमत लेता है। 500 से 1000 पंक्तियाँ एक बहु-पंक्तिVALUESमें मिलाएँ, या डेटाबेस के अपने bulk loader का उपयोग करें (PostgreSQLCOPY, MySQLLOAD DATA, SQLite transaction के भीतर बहु-पंक्ति inserts)। - Transaction इस्तेमाल करें। सब या कुछ नहीं, ताकि आधी लोड हुई तालिका किसी त्रुटि के बाद न बचे:
BEGIN;
INSERT INTO users (id, name) VALUES (1, 'Ada'), (2, 'Linus');
INSERT INTO order_items (order_id, sku, quantity) VALUES (1, 'A-1', 2);
COMMIT;
- Idempotent बनाएँ। आयात स्क्रिप्ट बार-बार चलाई जाती हैं। delete-then-insert के बजाय upsert इस्तेमाल करें:
| डेटाबेस | Idempotent रूप |
|---|---|
| PostgreSQL / SQLite | INSERT ... ON CONFLICT (id) DO UPDATE SET ... |
| MySQL | INSERT ... ON DUPLICATE KEY UPDATE ... |
ON CONFLICT के लिए unique constraint या primary key चाहिए, वरना वह कभी नहीं लगता। जब कोई प्राकृतिक key न हो, तो email जैसी व्यवसाय key पर unique index बनाएँ। जब बिल्कुल key न हो, तो JSON पक्ष पर स्थिर id जोड़ें — idempotency के लिए कभी auto-increment id पर निर्भर न रहें, क्योंकि दोबारा चलाने पर डुप्लिकेट insert होंगे।
8. बोली के अंतर एक नज़र में
| क्षमता | SQLite | PostgreSQL | MySQL |
|---|---|---|---|
बहु-पंक्ति VALUES |
हाँ | हाँ | हाँ |
| Upsert | ON CONFLICT |
ON CONFLICT |
ON DUPLICATE KEY UPDATE |
| Auto-increment key | INTEGER PRIMARY KEY |
GENERATED ... AS IDENTITY |
AUTO_INCREMENT |
| Boolean प्रकार | नहीं, 0/1 संग्रहीत करता है | boolean |
TINYINT(1) |
| Bulk आयात | transaction में बहु-पंक्ति inserts | COPY |
LOAD DATA |
| JSON समर्थन | TEXT और json1 फ़ंक्शन |
json / jsonb |
JSON |
| पहचानकर्ता quoting | दोहरे उद्धरण | दोहरे उद्धरण | backtick (डिफ़ॉल्ट) |
एक SQL फ़ाइल तीनों पर चलाने के लिए कम से कम ON CONFLICT, backtick और SERIAL से बचें। क्रॉस-डेटाबेस परियोजनाओं में schema और upsert कथन अलग-अलग उत्पन्न करें; एक स्क्रिप्ट सब पर फ़िट होने की अपेक्षा न रखें।
9. आयात के बाद तीन नमूना जाँच
स्रोत फ़ाइल हटाने से पहले तीन चीज़ें सत्यापित करें:
- पंक्ति संख्या।
SELECT COUNT(*) FROM users;JSON array की लंबाई के बराबर होनी चाहिए। एक पंक्ति गायब का अर्थ आमतौर पर अंतिम रिकॉर्ड कटा हुआ। - सबसे अधिक NULL दर वाला स्तंभ। यह असंगत key नाम या न खुले nesting की ओर इशारा करता है। लौटकर जाँचें कि स्रोत दोनों
userIdऔरuser_idइस्तेमाल कर रहा है या नहीं। - धनराशि और लंबे पहचानकर्ता। अधिकतम और न्यूनतम मान चुनें और पुष्टि करें कि न फ़्लोटिंग पॉइंट ने न integer रूपांतरण ने उन्हें काटा।
यदि पहले सारणीबद्ध आकार समझना हो, तो JSON, CSV और सारणीबद्ध डेटा रूपांतरण में दिया flattening तरीका इस्तेमाल करें। यदि दर्शक सहकर्मी हैं जिन्हें बस डेटा पढ़ना है, तो JSON को Excel में बदलने के चार तरीके डेटाबेस खड़ा करने से कम काम के हैं।
JSON को SQL में आयात करने के बाद संख्याएँ परिशुद्धता क्यों खो देती हैं?
2^53 से बड़े पूर्णांक JavaScript के JSON.parse के दौरान पहले ही परिशुद्धता खो चुके होते हैं, और उन्हें BIGINT में लिखने से वह वापस नहीं आती। ऐसे id JSON में स्ट्रिंग होने चाहिए और आयात के दौरान BIGINT या TEXT में बदले जाने चाहिए।
जब JSON में id न हो तो primary key के रूप में क्या इस्तेमाल करूँ?
डेटाबेस को एक उत्पन्न करने दें, या आयात से पहले स्थिर UUID बनाएँ। कभी array index को id के रूप में इस्तेमाल न करें: दोबारा चलाने पर डेटा का क्रम बदलता है और गलत पंक्तियाँ गलत रिकॉर्ड की ओर इशारा करती हैं। व्यवसाय key (email, ऑर्डर संख्या) पर unique index upsert भी सक्षम करता है।
Bulk INSERT में unique-key conflict आए तो क्या करूँ?
सादे INSERT को अपनी बोली के अनुरूप upsert से बदलें, और पुष्टि करें कि टकराने वाले स्तंभ पर unique constraint है। पहले delete और बाद में insert भी काम करता है, पर वह एक ही transaction में होना चाहिए और आपको कुछ समय के लिए खाली पंक्ति वाली विंडो स्वीकार करनी होगी।
Nested array के लिए हमेशा बाल तालिका ज़रूरी है?
नहीं। उप-आइटम से क्वेरी किए बिना केवल-पढ़ने वाले प्रदर्शन के लिए JSON स्तंभ सरल है। जैसे ही आपको tag या SKU से filter या समेकन करना हो, index वाली बाल तालिका टिकाऊ विकल्प है। निर्णायक तत्व यह है कि आप कैसे क्वेरी करते हैं, डेटा का आकार कैसा है नहीं।