हमने हाल ही में एक नई सहायता साइट लॉन्च की है, जहां आप प्रश्न पूछ सकते हैं, उत्पाद फ़ीडबैक या सुविधा अनुरोध सबमिट कर सकते हैं, या समर्थन टिकट खोल सकते हैं। लक्ष्य का एक हिस्सा उन सभी स्थानों को केंद्रीकृत करना था जहां हम समुदाय को सहायता प्रदान कर रहे थे। इसमें SQLPerformance.com प्रश्नोत्तर साइट शामिल है, जहां पॉल व्हाइट, ह्यूगो कॉर्नेलिस, और कई अन्य लोग आपके सबसे जटिल क्वेरी ट्यूनिंग और निष्पादन योजना प्रश्नों को हल करने में मदद कर रहे हैं, जो कि फरवरी 2013 तक सभी तरह से वापस जा रहे हैं। मैं आपको मिश्रित भावनाओं के साथ बताता हूं कि प्रश्नोत्तर साइट बंद कर दी गई है।
हालांकि, एक उल्टा है। अब आप उन कठिन प्रश्नों को नए समर्थन फ़ोरम पर पूछ सकते हैं। यदि आप पुरानी सामग्री की तलाश कर रहे हैं, तो यह अभी भी है, लेकिन यह थोड़ा अलग दिखता है। कई कारणों से मैं आज नहीं जाऊंगा, एक बार जब हमने मूल प्रश्नोत्तर साइट को सूर्यास्त करने का फैसला किया, तो हमने अंततः सभी मौजूदा सामग्री को केवल पढ़ने के लिए वर्डप्रेस साइट पर होस्ट करने का फैसला किया, बजाय इसे बैक एंड में माइग्रेट करने के नई साइट के।
यह पोस्ट उस निर्णय के पीछे के कारणों के बारे में नहीं है।
मुझे वास्तव में बहुत बुरा लगा कि उत्तर साइट को कितनी जल्दी ऑफ़लाइन आना पड़ा, डीएनएस स्विच किया गया, और सामग्री माइग्रेट हो गई। चूंकि साइट पर एक चेतावनी बैनर लागू किया गया था लेकिन AnswerHub ने वास्तव में इसे दृश्यमान नहीं बनाया, यह कई उपयोगकर्ताओं के लिए एक झटका था। इसलिए मैं यह सुनिश्चित करना चाहता था कि जितना हो सके मैं सामग्री को ठीक से रखता हूं, और मैं चाहता हूं कि यह सही हो। यह पोस्ट यहां है क्योंकि मैंने सोचा था कि वास्तविक प्रक्रिया के बारे में बात करना दिलचस्प होगा, इसे खींचने के साथ प्रौद्योगिकी के कितने अलग-अलग टुकड़े शामिल थे, और परिणाम दिखाने के लिए। मुझे उम्मीद नहीं है कि आप में से कोई भी इस एंड-टू-एंड से लाभान्वित होगा, क्योंकि यह एक अपेक्षाकृत अस्पष्ट प्रवासन पथ है, लेकिन एक कार्य को पूरा करने के लिए प्रौद्योगिकियों के एक समूह को एक साथ जोड़ने के उदाहरण के रूप में अधिक है। यह मेरे लिए एक अच्छा अनुस्मारक के रूप में भी कार्य करता है कि बहुत सी चीजें अंत में उतनी आसान नहीं होतीं जितनी वे शुरू करने से पहले लगती हैं।
TL;DR क्या यह है:मैंने संग्रहीत सामग्री को अच्छा दिखने के लिए समय और प्रयास का एक गुच्छा बिताया, हालांकि मैं अभी भी पिछले कुछ पदों को पुनर्प्राप्त करने का प्रयास कर रहा हूं जो अंत में आए थे। मैंने इन तकनीकों का उपयोग किया:
- पर्ली
- एसक्यूएल सर्वर
- पावरशेल
- ट्रांसमिट (एफ़टीपी)
- एचटीएमएल
- सीएसएस
- सी#
- मार्कडाउनशार्प
- phpMyAdmin
- MySQL
इसलिए शीर्षक। यदि आप रक्तरंजित विवरणों का एक बड़ा हिस्सा चाहते हैं, तो वे यहां हैं। यदि आपके कोई प्रश्न या प्रतिक्रिया है, तो कृपया नीचे पहुंचें या टिप्पणी करें।
AnswerHub ने Q&A सामग्री को होस्ट करने वाले MySQL डेटाबेस से 665 MB डंप फ़ाइल प्रदान की। मैंने हर संपादक को उस पर घुटने की कोशिश की, इसलिए मुझे पहले इसे जेरेड चेनी की इस आसान पर्ल स्क्रिप्ट का उपयोग करके प्रति तालिका एक फ़ाइल में तोड़ना पड़ा। मुझे जिन तालिकाओं की आवश्यकता थी उन्हें network11_nodes
. कहा जाता था (प्रश्न, उत्तर और टिप्पणियाँ), network11_authoritables
(उपयोगकर्ता), और network11_managed_files
(प्लान अपलोड सहित सभी अटैचमेंट):perl Extract_sql.pl -t network11_nodes -r dump.sql>>nodes.sql
perl Extract_sql.pl -t network11_authoritables -r dump.sql>> users.sql
perl Extract_sql.pl -t network11_managed_files -r dump.sql>> files.sql
अब वे SSMS में लोड करने के लिए बहुत तेज़ नहीं थे, लेकिन कम से कम वहाँ मैं Ctrl का उपयोग कर सकता था +एच इसे बदलने के लिए (उदाहरण के लिए):
CREATE TABLE `network11_managed_files` ( `c_id` bigint(20) NOT NULL, ... ); INSERT INTO `network11_managed_files` (`c_id`, ...) VALUES (1, ...);में सम्मिलित करें
इसके लिए:
CREATE TABLE dbo.files ( c_id bigint NOT NULL, ... ); INSERT dbo.files (c_id, ...) VALUES (1, ...);
तब मैं डेटा को SQL सर्वर में लोड कर सकता था ताकि मैं इसमें हेरफेर कर सकूं। और मेरा विश्वास करो, मैंने इसमें हेरफेर किया।
इसके बाद, मुझे सभी अनुलग्नकों को पुनः प्राप्त करना था। देखें, विक्रेता से मिली MySQL डंप फ़ाइल में एक gazillion INSERT
है बयान, लेकिन उपयोगकर्ताओं द्वारा अपलोड की गई वास्तविक योजना फ़ाइलों में से कोई भी नहीं - डेटाबेस में केवल फाइलों के सापेक्ष पथ थे। मैंने पावरशेल कमांड की एक श्रृंखला बनाने के लिए टी-एसक्यूएल का इस्तेमाल किया जो Invoke-WebRequest
. को कॉल करेगा सभी फाइलों को पुनः प्राप्त करने और उन्हें स्थानीय रूप से स्टोर करने के लिए (इस बिल्ली को त्वचा के कई तरीके, लेकिन यह ड्रॉप डेड आसान था)। इससे:
SELECT 'Invoke-WebRequest -Uri ' + '"$($url)' + RTRIM(c_id) + '-' + c_name + '"' + ' -OutFile "E:\s\temp\' + RTRIM(c_id) + '-' + c_name + '";' FROM dbo.files WHERE LOWER(c_mime_type) LIKE 'application/%';
इसने आदेशों के इस सेट को प्राप्त किया (इस टीएलएस मुद्दे को हल करने के लिए पूर्व-आदेश के साथ); सब कुछ बहुत तेज़ी से चला, लेकिन मैं {मैसिव सेट ऑफ़ फाइल्स} और/या {लो बैंडविड्थ} के किसी भी संयोजन के लिए इस दृष्टिकोण की अनुशंसा नहीं करता:
$AllProtocols = [System.Net.SecurityProtocolType]'Ssl3,Tls,Tls11,Tls12'; [System.Net.ServicePointManager]::SecurityProtocol = $AllProtocols; $u = "https://answers.sqlperformance.com/s/temp/"; Invoke-WebRequest -Uri "$($u)/1-proc.pesession" -OutFile "E:\s\temp\1-proc.pesession"; Invoke-WebRequest -Uri "$($u)/14-test.pesession" -OutFile "E:\s\temp\14-test.pesession"; Invoke-WebRequest -Uri "$($u)/15-a.QueryAnalysis" -OutFile "E:\s\temp\15-a.QueryAnalysis"; ...
इसने लगभग सभी अनुलग्नकों को डाउनलोड कर लिया था, लेकिन माना जाता है कि कुछ पुरानी साइट पर त्रुटियों के कारण छूट गए थे जब उन्हें शुरू में अपलोड किया गया था। इसलिए, नई साइट पर, आप कभी-कभी किसी ऐसे अटैचमेंट का संदर्भ देख सकते हैं जो मौजूद नहीं है।
फिर मैंने temp
. अपलोड करने के लिए पैनिक ट्रांसमिट 5 का इस्तेमाल किया नई साइट पर फ़ोल्डर, और अब जब सामग्री अपलोड हो जाती है, तो /s/temp/1-proc.pesession
से लिंक हो जाता है काम करना जारी रखेगा।
इसके बाद, मैं एसएसएल पर चला गया। नई वर्डप्रेस साइट पर एक प्रमाणपत्र का अनुरोध करने के लिए, हमें अपने वर्डप्रेस होस्ट, WPEngine पर CNAME को इंगित करने के लिए answer.sqlperformance.com के लिए DNS को अपडेट करना पड़ा। यह यहाँ चिकन और अंडे की तरह था - हमें https URL के लिए कुछ डाउनटाइम भुगतना पड़ा, जो नई साइट पर बिना किसी प्रमाण पत्र के विफल हो जाएगा। यह ठीक था क्योंकि पुरानी साइट पर प्रमाणपत्र की समय सीमा समाप्त हो गई थी, इसलिए वास्तव में, हम बदतर नहीं थे। मुझे ऐसा करने के लिए तब तक इंतजार करना पड़ा जब तक कि मैंने पुरानी साइट से सभी फाइलें डाउनलोड नहीं कर लीं, क्योंकि एक बार डीएनएस के पलट जाने के बाद, कुछ पिछले दरवाजे के अलावा उन तक पहुंचने का कोई रास्ता नहीं होगा।
जब मैं डीएनएस के प्रचार-प्रसार की प्रतीक्षा कर रहा था, मैंने वर्डप्रेस में उपभोग योग्य सभी प्रश्नों, उत्तरों और टिप्पणियों को खींचने के लिए तर्क पर काम करना शुरू कर दिया। टेबल स्कीमा न केवल वर्डप्रेस से अलग थे, बल्कि संस्थाओं के प्रकार भी काफी भिन्न हैं। मेरी दृष्टि प्रत्येक प्रश्न - और किसी भी उत्तर और / या टिप्पणियों को - एक ही पोस्ट में संयोजित करना था।
मुश्किल हिस्सा यह है कि नोड्स तालिका में माता-पिता और मूल ("मास्टर") मूल संदर्भों के साथ एक ही तालिका में सभी तीन सामग्री प्रकार होते हैं। उनका फ्रंट-एंड कोड संभवतः किसी प्रकार के कर्सर का उपयोग करता है और सामग्री को एक पदानुक्रमित और कालानुक्रमिक क्रम में प्रदर्शित करता है। मेरे पास वर्डप्रेस में वह विलासिता नहीं होगी, इसलिए मुझे एक शॉट में HTML को एक साथ स्ट्रिंग करना पड़ा। उदाहरण के तौर पर, यहां डेटा कैसा दिखता है:
SELECT c_type, c_id, c_parent, oParent = c_originalParent, c_creation_date, c_title FROM dbo.nodes WHERE c_originalParent = 285; /* c_type c_id c_parent oParent c_creation_date accepted c_title ---------- ------ -------- ------- ---------------- -------- ------------------------- question 285 NULL 285 2013-02-13 16:30 why is the MERGE JOIN ... answer 287 285 285 2013-02-14 01:15 1 NULL comment 289 285 285 2013-02-14 13:35 NULL answer 293 285 285 2013-02-14 18:22 NULL comment 294 287 285 2013-02-14 18:29 NULL comment 298 285 285 2013-02-14 20:40 NULL comment 299 298 285 2013-02-14 18:29 NULL */
मैं आईडी, या प्रकार, या माता-पिता द्वारा आदेश नहीं दे सका, क्योंकि कभी-कभी एक टिप्पणी बाद में पहले के उत्तर पर आती है, पहला उत्तर हमेशा स्वीकृत उत्तर नहीं होगा, और इसी तरह। मुझे यह आउटपुट चाहिए था (जहां ++
इंडेंट के एक स्तर का प्रतिनिधित्व करता है):
/* c_type c_id c_parent oParent c_creation_date reason ---------- ------ -------- ------- ---------------- ------------------------- question 285 NULL 285 2013-02-13 16:30 question is ALWAYS first ++comment 289 285 285 2013-02-14 13:35 comments on the question before answers answer 287 285 285 2013-02-14 01:15 first answer (accepted = 1) ++comment 294 287 285 2013-02-14 18:29 first comment on first answer ++comment 298 287 285 2013-02-14 20:40 second comment on first answer ++++comment 299 298 285 2013-02-14 18:29 reply to second comment on first answer answer 293 285 285 2013-02-14 18:22 second answer */
मैंने एक पुनरावर्ती सीटीई लिखना शुरू किया और, <स्ट्राइकथ्रू>आंशिक रूप सेस्ट्राइकथ्रू> उस शाम बहुत अधिक Rekorderlig के कारण, मैंने साथी उत्पाद प्रबंधक, Andy Mallon (@AMtwo) की मदद ली। उन्होंने मुझे इस क्वेरी को तैयार करने में मदद की, जो पदों को उनके उचित प्रदर्शन क्रम में वापस कर देगा (और आप इस स्निपेट को आज़मा सकते हैं, माता-पिता और/या स्वीकृत उत्तर को बदलकर, यह देखने के लिए कि सही क्रम अभी भी वापस किया जाएगा):पी>
DECLARE @foo TABLE ( c_type varchar(255), c_id int, c_parent int, oParent int, accepted bit ); INSERT @foo(c_type, c_id, c_parent, oParent, accepted) VALUES ('question', 285, NULL, 285, 0), ('answer', 287, 285 , 285, 1), ('comment', 289, 285 , 285, 0), ('comment', 294, 287 , 285, 0), ('comment', 298, 287 , 285, 0), ('comment', 299, 298 , 285, 0), ('answer', 293, 285 , 285, 0); ;WITH cte AS ( SELECT lvl = 0, f.c_type, f.c_id, f.c_parent, f.oParent, Sort = CONVERT(varchar(255),RIGHT('00000' + CONVERT(varchar(5),f.c_id),5)) FROM @foo AS f WHERE f.c_parent IS NULL UNION ALL SELECT lvl = c.lvl + 1, c_type = CONVERT(varchar(255), CASE WHEN f.accepted = 1 THEN 'accepted answer' WHEN f.c_type = 'comment' THEN c.c_type + ' ' + f.c_type ELSE f.c_type END), f.c_id, f.c_parent, f.oParent, Sort = CONVERT(varchar(255),c.Sort + RIGHT('00000' + CONVERT(varchar(5),f.c_id),5)) FROM @foo AS f INNER JOIN cte AS c ON c.c_id = f.c_parent ) SELECT lvl = CASE lvl WHEN 0 THEN 1 ELSE lvl END, c_type, c_id, c_parent, oParent, Sort FROM cte ORDER BY oParent, CASE WHEN c_type LIKE 'question%' THEN 1 -- it's a question *or* a comment on the question WHEN c_type LIKE 'accepted answer%' THEN 2 -- accepted answer *or* comment on accepted answer ELSE 3 END, Sort;
परिणाम:
/* lvl c_type c_id c_parent oParent Sort ---- --------------------------------- ----------- ----------- ----------- -------------------- 1 question 285 NULL 285 00285 1 question comment 289 285 285 0028500289 1 accepted answer 287 285 285 0028500287 2 accepted answer comment 294 287 285 002850028700294 2 accepted answer comment 298 287 285 002850028700298 3 accepted answer comment comment 299 298 285 00285002870029800299 1 answer 293 285 285 0028500293 */
प्रतिभावान। मैंने एक दर्जन या तो अन्य की जाँच की, और अगले कदम पर आगे बढ़ने में खुशी हुई। मैंने कई बार एंडी को धन्यवाद दिया है, लेकिन मुझे इसे फिर से करने दें:धन्यवाद एंडी!
अब जबकि मैं अपने पसंद के क्रम में पूरे सेट को वापस कर सकता था, मुझे HTML तत्वों और वर्ग नामों को लागू करने के लिए आउटपुट में कुछ हेरफेर करना पड़ा, जो मुझे प्रश्नों, उत्तरों, टिप्पणियों और इंडेंटेशन को सार्थक तरीके से चिह्नित करने देगा। अंतिम लक्ष्य आउटपुट था जो इस तरह दिखता था (और ध्यान रखें, यह सरल मामलों में से एक है):
<div class="question"> <span class="authorq" title=" Author : author name "> <i class="fas fa-user"></i>Author name</span> <span class="createdq" title=" February 13th, 2013 "> <i class="fas fa-calendar-alt"></i>2013-02-13 16:30:36</span> <div class=mainbodyq>I don't understand why the merge operator is passing over 4million rows to the hash match operator when there is only 41K and 19K from other operators. <div class=attach><i class="fas fa-file"></i> <a target="_blank" href="/s/temp/254-tmp4DA0.queryanalysis" rel="noopener noreferrer"> /s/temp/254-tmp4DA0.queryanalysis</a> </div> </div> <div class="comment indent1 "> <div class=linecomment> <span class="authorc" title=" Author : author name "> <i class="fas fa-user"></i>author name</span> <span class="createdc" title=" February 14th, 2013 "> <i class="fas fa-calendar-alt"></i>2013-02-14 13:35:39</span> </div> <div class=mainbodyc> I am still trying to understand the significant amount of rows from the MERGE operator. Unless it's a result of a Cartesian product from the two inputs then finally the WHERE predicate is applied to filter out the unmatched rows leaving the 4 million row count. </div> </div> <div class="answer indent1 [accepted]"> <div class=lineanswer> <span class="authora" title=" Author : author name "> <i class="fas fa-user"></i>author name</span> <span class="createda" title=" February 14th, 2013 "> <i class="fas fa-calendar-alt"></i>2013-02-14 01:15:42</span> </div> <div class=mainbodya> The reason for the large number of rows can be seen in the Plan Explorer tool tip for the Merge Join operator: <img src="/s/temp/259-sp.png" alt="Merge Join tool tip" /> ... </div> </div> </div>
मैं सभी 5,000+ वस्तुओं के लिए उस आउटपुट के एक विश्वसनीय रूप पर उतरने के लिए पुनरावृत्तियों की हास्यास्पद संख्या के माध्यम से कदम नहीं उठाऊंगा (जिसका अनुवाद लगभग 1,000 पदों पर एक बार सब कुछ एक साथ चिपका हुआ था)। इसके अलावा, मुझे इन्हें INSERT
. के रूप में जेनरेट करना था बयान जो मैं फिर वर्डप्रेस साइट पर phpMyAdmin में पेस्ट कर सकता था, जिसका अर्थ था उनके विचित्र वाक्यविन्यास आरेख का पालन करना। उन बयानों में वर्डप्रेस द्वारा आवश्यक अन्य अतिरिक्त जानकारी शामिल करने की आवश्यकता है, लेकिन स्रोत डेटा में मौजूद या सटीक नहीं है (जैसे post_type
) और वह व्यवस्थापक कंसोल बहुत अधिक डेटा दिए जाने का समय समाप्त कर देगा, इसलिए मुझे इसे एक बार में ~ 750 प्रविष्टियों में विभाजित करना पड़ा। यहां वह प्रक्रिया है जिसके साथ मैंने समाप्त किया (यह वास्तव में कुछ भी विशिष्ट सीखने के लिए नहीं है, केवल एक प्रदर्शन है कि आयातित डेटा में कितना हेरफेर आवश्यक था):
CREATE /* OR ALTER */ PROCEDURE dbo.BuildMySQLInserts @LowerBound int = 1, @UpperBound int = 750 AS BEGIN SET NOCOUNT ON; ;WITH CTE AS ( SELECT lvl = 0, [type] = CONVERT(varchar(100),f.[type]), f.id, f.parent, f.master_parent, created = CONVERT(char(10), f.created, 120) + ' ' + CONVERT(char(8), f.created, 108), f.state, Sort = CONVERT(varchar(100),RIGHT('0000000000' + CONVERT(varchar(10),f.id),10)) FROM dbo.foo AS f WHERE f.type = 'question' AND master_parent BETWEEN @LowerBound AND @UpperBound UNION ALL SELECT lvl = c.lvl + 1, CONVERT(varchar(100),CASE WHEN f.[state] = '[accepted]' THEN 'accepted answer' WHEN f.type = 'comment' THEN c.type + ' ' + f.type ELSE f.type END), f.id, f.parent, f.master_parent, created = CONVERT(char(10), f.created, 120) + ' ' + CONVERT(char(8), f.created, 108), f.state, Sort = CONVERT(varchar(100),c.sort + RIGHT('0000000000' + CONVERT(varchar(10),f.id),10)) FROM dbo.foo AS f JOIN CTE AS c ON c.id = f.parent ) SELECT master_parent, prefix = CASE WHEN lvl = 0 THEN CONVERT(varchar(11), master_parent) + ', 3, ''' + created + ''', ''' + created + ''',''' END, bodypre = '<div class="' + COALESCE(c_type, RTRIM(LEFT([type],8))) + CASE WHEN c_type <> 'question' THEN ' indent' + RTRIM(lvl) + COALESCE(' ' + [state], '') ELSE '' END + '">' + CASE WHEN c_type <> 'question' THEN '<div class=line' + c_type + '>' ELSE '' END + '<span class="author' + LEFT(c_type, 1) + '" title=" Author : ' + REPLACE(REPLACE(Fullname,'''','\'''),'"','') + ' "><i class="fas fa-user"></i>' + REPLACE(Fullname,'''','\''') --" + '</span> <span class="created' + LEFT(c_type,1) + '" title=" ' + DATENAME(MONTH, c_creation_date) + ' ' + RTRIM(DAY(c_creation_date)) + CASE WHEN DAY(c_creation_date) IN (1,21,31) THEN 'st' WHEN DAY(c_creation_date) IN (2,22) THEN 'nd' WHEN DAY(c_creation_date) IN (3,23) THEN 'rd' ELSE 'th' END + ', ' + RTRIM(YEAR(c_creation_date)) + ' "><i class="fas fa-calendar-alt"></i>' + created + '</span>' + CASE WHEN c_type <> 'question' THEN '</div>' ELSE '' END, body = '<div class=mainbody' + left(c_type,1) + '>' + REPLACE(REPLACE(c_body, char(39), '\' + char(39)), '’', '\' + char(39)), bodypost = COALESCE(urls, '') + '</div></div>',--' + CASE WHEN c_type = 'question' THEN '</div>' ELSE '' END, suffix = ''',''' + REPLACE(n.c_title, '''', '\''') + ''','''',''publish'', ''closed'',''closed'','''',''' + REPLACE(n.c_plug, '''', '\''') + ''','''','''',''' + created + ''',''' + created + ''','''',0, ''https://answers.sqlperformance.com/?p=' + CONVERT(varchar(11), master_parent) + ''', 0, ''post'','''',0);', rn = RTRIM(ROW_NUMBER() OVER (PARTITION BY master_parent ORDER BY master_parent, CASE WHEN [type] LIKE 'question%' THEN 1 WHEN [type] LIKE 'accepted answer%' THEN 2 ELSE 3 END, Sort)), c = RTRIM(COUNT(*) OVER (PARTITION BY master_parent)) FROM CTE LEFT OUTER JOIN dbo.network11_nodes AS n ON cte.id = n.c_id LEFT OUTER JOIN dbo.Users AS u ON n.c_author = u.UserID LEFT OUTER JOIN ( SELECT NodeID, urls = STRING_AGG('<div class=attach> <i class="fas fa-file' + CASE WHEN c_mime_type IN ('image/jpeg','image/png') THEN '-image' ELSE '' END + '"></i><a target="_blank" href=' + url + ' rel="noopener noreferrer">' + url + '</a></div>', '\n') FROM dbo.Attachments GROUP BY NodeID ) AS a ON n.c_id = a.NodeID ORDER BY master_parent, CASE WHEN [type] LIKE 'question%' THEN 1 WHEN [type] LIKE 'accepted answer%' THEN 2 ELSE 3 END, Sort; END GO
इसका आउटपुट अभी पूरा नहीं हुआ है और अभी तक वर्डप्रेस में स्टफ करने के लिए तैयार नहीं है:
नमूना आउटपुट (विस्तार करने के लिए क्लिक करें)
मुझे वास्तविक सामग्री (मार्कडाउन सहित) को HTML और CSS में बदलने के लिए C# से कुछ अतिरिक्त सहायता की आवश्यकता होगी, जिसे मैं बेहतर नियंत्रित कर सकता था, और आउटपुट (INSERT
का एक गुच्छा) लिख सकता था। बयान जो HTML कोड का एक गुच्छा शामिल करने के लिए हुआ) डिस्क पर फ़ाइलों के लिए मैं खोल सकता था और phpMyAdmin में पेस्ट कर सकता था। HTML के लिए, सादा पाठ + मार्कडाउन जो इस तरह शुरू हुआ:
dbo.sometable से कुछ चुनें;
[1]:https://कहीं और
इसे बनने की आवश्यकता होगी:
इसके बारे में एक ब्लॉग पोस्ट है, और यह पोस्ट भी है।
dbo.sometable से कुछ चुनें;
इसे दूर करने के लिए, मैंने मार्कडाउनशर्प की मदद ली, जो एक ओपन सोर्स लाइब्रेरी है जो स्टैक ओवरफ्लो से उत्पन्न होती है जो मार्कडाउन-टू-एचटीएमएल रूपांतरण को संभालती है। यह मेरी ज़रूरतों के लिए अच्छा था, लेकिन सही नहीं था; मुझे अभी भी और हेरफेर करना होगा:
- MarkdownSharp
target=_blank
. जैसी चीज़ों की अनुमति नहीं देता है , इसलिए प्रसंस्करण के बाद मुझे उन्हें स्वयं इंजेक्ट करना होगा; - कोड (चार रिक्त स्थान के साथ कुछ भी) इनहेरिट करता है
using System.Text; using System.Data; using System.Data.SqlClient; using MarkdownSharp; using System.IO; namespace AnswerHubMigrator { class Program { static void Main(string[] args) { StringBuilder output; string suffix = ""; string thisfile = ""; // pass two arguments on the command line, e.g. 1, 750 int LowerBound = int.Parse(args[0]); int UpperBound = int.Parse(args[1]); // auto-expand URLs, and only accept bold/italic markdown // when it completely surrounds an entire word var options = new MarkdownOptions { AutoHyperlink = true, StrictBoldItalic = true }; MarkdownSharp.Markdown mark = new MarkdownSharp.Markdown(options); using (var conn = new SqlConnection("Server=.\\SQL2017;Integrated Security=true")) using (var cmd = new SqlCommand("MigrateDB.dbo.BuildMySQLInserts", conn)) { cmd.CommandType = CommandType.StoredProcedure; cmd.Parameters.Add("@LowerBound", SqlDbType.Int).Value = LowerBound; cmd.Parameters.Add("@UpperBound", SqlDbType.Int).Value = UpperBound; conn.Open(); using (var reader = cmd.ExecuteReader()) { // use a StringBuilder to dump output to a file output = new StringBuilder(); while (reader.Read()) { // on first pass, make a new delete/insert // delete is to make the commands idempotent if (reader["rn"].Equals("1")) { // for each master parent, I would create a // new WordPress post, inheriting the parent ID output.Append("DELETE FROM `wp_posts` WHERE ID = "); output.Append(reader["master_parent"].ToString()); output.Append("; INSERT INTO `wp_posts` (`ID`, `post_author`, "); output.Append("`post_date`, `post_date_gmt`, `post_content`, "); output.Append("`post_title`, `post_excerpt`, `post_status`, "); output.Append("`comment_status`, `ping_status`, `post_password`,"); output.Append(" `post_name`, `to_ping`, `pinged`, `post_modified`,"); output.Append(" `post_modified_gmt`, `post_content_filtered`, "); output.Append("`post_parent`, `guid`, `menu_order`, `post_type`, "); output.Append("`post_mime_type`, `comment_count`) VALUES ("); // I'm sure some of the above columns are optional, but identifying // those would not be a valuable use of time IMHO output.Append(reader["prefix"]); // hold on to the additional values until last row suffix = reader["suffix"].ToString(); } // manipulate the body content to be WordPress and INSERT statement-friendly string body = reader["body"].ToString().Replace(@"\n", "\n"); body = mark.Transform(body).Replace("href=", "target=_blank href="); body = body.Replace("<p>", "").Replace("</p>", ""); body = body.Replace("<pre><code>", "<pre lang=\"tsql\">"); body = body.Replace("</code></"+"pre>", "</"+"pre>"); body = body.Replace(@"'", "\'").Replace(@"’", "\'"); body = reader["bodypre"].ToString() + body.Replace("\n", @"\n"); body += reader["bodypost"].ToString(); body = body.Replace("<", "<").Replace(">", ">"); output.Append(body); // if we are on the last row, add additional values from the first row if (reader["c"].Equals(reader["rn"])) { output.Append(suffix); } } thisfile = UpperBound.ToString(); using (StreamWriter w = new StreamWriter(@"C:\wp\" + thisfile + ".sql")) { w.WriteLine(output); w.Flush(); } } } } } }
हां, यह कोड का एक बदसूरत गुच्छा है, लेकिन अंत में यह मुझे आउटपुट के सेट पर ले गया जो phpMyAdmin प्यूक नहीं बनायेगा, और यह कि वर्डप्रेस अच्छी तरह से (पर्याप्त) पेश करेगा। मैंने अलग-अलग पैरामीटर श्रेणियों के साथ कई बार C# प्रोग्राम को बस कॉल किया:
AnswerHubMigrator 1 750 AnswerHubMigrator 751 1500 AnswerHubMigrator 1501 2250 ...
फिर मैंने प्रत्येक फाइल खोली, उन्हें phpMyAdmin में चिपकाया, और GO को हिट किया:
phpMyAdmin (विस्तार करने के लिए क्लिक करें)बेशक मुझे प्रश्नों, टिप्पणियों और उत्तरों के बीच अंतर करने में मदद करने के लिए वर्डप्रेस के भीतर कुछ सीएसएस जोड़ना पड़ा, और दोनों सवालों और जवाबों के जवाब दिखाने के लिए टिप्पणियों को इंडेंट करना, टिप्पणियों का जवाब देने वाली घोंसला टिप्पणियां, और इसी तरह। जब आप एक महीने के प्रश्नों का गहराई से अध्ययन करते हैं तो एक अंश कैसा दिखता है:
प्रश्न टाइल (विस्तार करने के लिए क्लिक करें)और फिर एक उदाहरण पोस्ट, एम्बेडेड चित्र, एकाधिक अनुलग्नक, नेस्टेड टिप्पणियां, और एक उत्तर दिखा रहा है:
नमूना प्रश्न और उत्तर (वहां जाने के लिए क्लिक करें)मैं अभी भी कुछ पोस्ट पुनर्प्राप्त करने का प्रयास कर रहा हूं जो अंतिम बैकअप लेने के बाद साइट पर सबमिट किए गए थे, लेकिन मैं आपका स्वागत करता हूं कि आप चारों ओर ब्राउज़ करें। कृपया हमें बताएं कि क्या आपको कुछ गुम या जगह से बाहर है, या यहां तक कि हमें यह बताने के लिए कि सामग्री अभी भी आपके लिए उपयोगी है। हम प्लान एक्सप्लोरर के भीतर से प्लान अपलोड कार्यक्षमता को फिर से पेश करने की उम्मीद करते हैं, लेकिन इसके लिए नई सहायता साइट पर कुछ एपीआई काम करने की आवश्यकता होगी, इसलिए आज मेरे पास आपके लिए ईटीए नहीं है।
- जवाब.SQLPerformance.com