Kami baru-baru ini meluncurkan situs dukungan baru, tempat Anda dapat mengajukan pertanyaan, mengirimkan umpan balik produk atau permintaan fitur, atau membuka tiket dukungan. Bagian dari tujuannya adalah untuk memusatkan semua tempat di mana kami menawarkan bantuan kepada masyarakat. Ini termasuk situs Q&A SQLPerformance.com, di mana Paul White, Hugo Kornelis, dan banyak lainnya telah membantu memecahkan pertanyaan penyetelan kueri dan rencana eksekusi Anda yang paling rumit, sejak Februari 2013. Saya memberi tahu Anda dengan perasaan campur aduk bahwa Situs Tanya Jawab telah ditutup.
Ada sisi positifnya. Sekarang Anda dapat mengajukan pertanyaan sulit tersebut di forum dukungan baru. Jika Anda mencari konten lama, yah, itu masih ada, tetapi tampilannya sedikit berbeda. Untuk berbagai alasan yang tidak akan saya bahas hari ini, setelah kami memutuskan untuk menghentikan situs Tanya Jawab asli, kami akhirnya memutuskan untuk hanya meng-host semua konten yang ada di situs WordPress read-only, daripada memigrasikannya ke back end dari situs baru.
Postingan ini bukan tentang alasan di balik keputusan itu.
Saya merasa sangat buruk tentang seberapa cepat situs jawaban harus offline, DNS diaktifkan, dan konten dimigrasikan. Karena spanduk peringatan diterapkan di situs tetapi AnswerHub tidak benar-benar membuatnya terlihat, ini mengejutkan banyak pengguna. Jadi saya ingin memastikan bahwa saya menyimpan sebanyak mungkin konten dengan benar, dan saya ingin itu benar. Posting ini ada di sini karena saya pikir akan menarik untuk membicarakan proses sebenarnya, berapa banyak teknologi berbeda yang terlibat dengan menariknya, dan untuk memamerkan hasilnya. Saya tidak berharap ada di antara Anda yang mendapat manfaat dari ujung ke ujung ini, karena ini adalah jalur migrasi yang relatif tidak jelas, tetapi lebih sebagai contoh mengikat sekelompok teknologi bersama-sama untuk menyelesaikan tugas. Ini juga berfungsi sebagai pengingat yang baik untuk diri saya sendiri bahwa banyak hal tidak semudah yang terdengar sebelum Anda mulai.
TL;DR adalah ini:Saya menghabiskan banyak waktu dan upaya untuk membuat konten yang diarsipkan terlihat bagus, meskipun saya masih mencoba memulihkan beberapa posting terakhir yang masuk menjelang akhir. Saya menggunakan teknologi ini:
- Perl
- SQL Server
- PowerShell
- Kirim (FTP)
- HTML
- CSS
- C#
- Penurunan harga yang tajam
- phpMyAdmin
- MySQL
Makanya judulnya. Jika Anda ingin potongan besar dari detail berdarah, ini dia. Jika Anda memiliki pertanyaan atau masukan, silakan hubungi atau beri komentar di bawah.
AnswerHub menyediakan file dump 665 MB dari database MySQL yang menampung konten T&J. Setiap editor yang saya coba mencekiknya, jadi pertama-tama saya harus memecahnya menjadi file per tabel menggunakan skrip Perl yang praktis dari Jared Cheney ini. Tabel yang saya butuhkan disebut network11_nodes
(pertanyaan, jawaban, dan komentar), network11_authoritables
(pengguna), dan network11_managed_files
(semua lampiran, termasuk unggahan paket):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
Sekarang itu tidak terlalu cepat untuk dimuat di SSMS, tapi setidaknya di sana saya bisa menggunakan Ctrl +H untuk mengubah (misalnya) ini:
CREATE TABLE `network11_managed_files` ( `c_id` bigint(20) NOT NULL, ... ); INSERT INTO `network11_managed_files` (`c_id`, ...) VALUES (1, ...);
Untuk ini:
CREATE TABLE dbo.files ( c_id bigint NOT NULL, ... ); INSERT dbo.files (c_id, ...) VALUES (1, ...);
Kemudian saya bisa memuat data ke SQL Server sehingga saya bisa memanipulasinya. Dan percayalah, saya memanipulasinya.
Selanjutnya, saya harus mengambil semua lampiran. Lihat, file dump MySQL yang saya dapatkan dari vendor berisi trilyun INSERT
pernyataan, tetapi tidak ada file rencana aktual yang telah diunggah pengguna — database hanya memiliki jalur relatif ke file tersebut. Saya menggunakan T-SQL untuk membuat serangkaian perintah PowerShell yang akan memanggil Invoke-WebRequest
untuk mengambil semua file dan menyimpannya secara lokal (banyak cara untuk menguliti kucing ini, tetapi ini sangat mudah). Dari ini:
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/%';
Itu menghasilkan kumpulan perintah ini (bersama dengan pra-perintah untuk menyelesaikan masalah TLS ini); semuanya berjalan cukup cepat, tetapi saya tidak menyarankan pendekatan ini untuk kombinasi {massive set of files} dan/atau {low bandwidth}:
$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"; ...
Ini mengunduh hampir semua lampiran tetapi, memang, ada beberapa yang terlewatkan karena kesalahan di situs lama saat pertama kali diunggah. Jadi, di situs baru, Anda mungkin sesekali melihat referensi ke lampiran yang tidak ada.
Kemudian saya menggunakan Panic Transmit 5 untuk mengunggah temp
folder ke situs baru, dan sekarang ketika konten diunggah, tautan ke /s/temp/1-proc.pesession
akan terus bekerja.
Selanjutnya, saya pindah ke SSL. Untuk meminta sertifikat di situs WordPress baru, kami harus memperbarui DNS untuk answer.sqlperformance.com agar mengarah ke CNAME di host WordPress kami, WPEngine. Ini seperti ayam dan telur di sini — kami harus mengalami downtime untuk URL https, yang akan gagal tanpa sertifikat di situs baru. Ini baik-baik saja karena sertifikat di situs lama telah kedaluwarsa, jadi sungguh, kami tidak lebih buruk. Saya juga harus menunggu untuk melakukan ini sampai saya mengunduh semua file dari situs lama, karena begitu DNS dibalik, tidak akan ada cara untuk mengaksesnya kecuali melalui pintu belakang.
Sementara saya menunggu DNS menyebar, saya mulai mengerjakan logika untuk menarik semua pertanyaan, jawaban, dan komentar menjadi sesuatu yang dapat dikonsumsi di WordPress. Tidak hanya skema tabel yang berbeda dari WordPress, jenis entitas juga sangat berbeda. Visi saya adalah menggabungkan setiap pertanyaan — dan jawaban dan/atau komentar apa pun — menjadi satu postingan.
Bagian yang sulit adalah bahwa tabel node hanya berisi ketiga tipe konten dalam tabel yang sama, dengan referensi induk dan induk asli ("master"). Kode front-end mereka kemungkinan menggunakan semacam kursor untuk melangkah dan menampilkan konten dalam urutan hierarkis dan kronologis. Saya tidak akan memiliki kemewahan itu di WordPress, jadi saya harus merangkai HTML bersama dalam satu kesempatan. Sebagai contoh, berikut tampilan datanya:
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 */
Saya tidak dapat mengurutkan berdasarkan id, atau jenis, atau berdasarkan orang tua, karena terkadang komentar akan muncul kemudian pada jawaban yang lebih awal, jawaban pertama tidak selalu merupakan jawaban yang diterima, dan seterusnya. Saya ingin keluaran ini (di mana ++
mewakili satu tingkat indentasi):
/* 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 */
Saya mulai menulis CTE rekursif dan,
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;
Hasil:
/* 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 */
jenius. Saya melihat selusin atau lebih yang lain, dan senang bisa melanjutkan ke langkah berikutnya. Saya telah mengucapkan banyak terima kasih kepada Andy, beberapa kali, tetapi izinkan saya melakukannya lagi:Terima kasih Andy!
Sekarang saya dapat mengembalikan seluruh rangkaian dalam urutan yang saya suka, saya harus melakukan beberapa manipulasi output untuk menerapkan elemen HTML dan nama kelas yang memungkinkan saya menandai pertanyaan, jawaban, komentar, dan lekukan dengan cara yang berarti. Tujuan akhirnya adalah keluaran yang terlihat seperti ini (dan perlu diingat, ini adalah salah satu kasus yang lebih sederhana):
<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>
Saya tidak akan melewati jumlah iterasi konyol yang harus saya lalui untuk mendapatkan bentuk output yang andal untuk semua 5.000+ item (yang diterjemahkan menjadi hampir 1.000 posting setelah semuanya direkatkan). Selain itu, saya perlu membuat ini dalam bentuk INSERT
pernyataan yang kemudian dapat saya tempel ke phpMyAdmin di situs WordPress, yang berarti mengikuti diagram sintaks yang aneh. Pernyataan tersebut perlu menyertakan informasi tambahan lain yang diperlukan oleh WordPress, tetapi tidak ada atau akurat dalam data sumber (seperti post_type
). Dan konsol admin itu akan kehabisan waktu karena terlalu banyak data, jadi saya harus membaginya menjadi ~750 sisipan sekaligus. Berikut adalah prosedur yang saya dapatkan (ini bukan untuk mempelajari sesuatu yang spesifik, hanya demonstrasi seberapa banyak manipulasi data yang diimpor diperlukan):
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
Output dari itu belum selesai dan belum siap untuk dimasukkan ke WordPress dulu:
Contoh keluaran (klik untuk memperbesar)
Saya memerlukan bantuan tambahan dari C# untuk mengubah konten aktual (termasuk penurunan harga) menjadi HTML dan CSS yang dapat saya kendalikan dengan lebih baik, dan menulis hasilnya (sekelompok INSERT
pernyataan yang kebetulan menyertakan banyak kode HTML) ke file di disk yang bisa saya buka dan tempel ke phpMyAdmin. Untuk HTML, teks biasa + penurunan harga yang dimulai seperti ini:
PILIH sesuatu dari dbo.sometable;
[1]:https://tempat lain
Perlu menjadi ini:
Ada postingan blog di sini yang membicarakannya, dan juga postingan ini .
PILIH sesuatu dari dbo.sometable;
Untuk melakukan ini, saya meminta bantuan MarkdownSharp, perpustakaan sumber terbuka yang berasal dari Stack Overflow yang menangani banyak konversi penurunan harga ke HTML. Itu cocok untuk kebutuhan saya, tetapi tidak sempurna; Saya masih harus melakukan manipulasi lebih lanjut:
- MarkdownSharp tidak mengizinkan hal-hal seperti
target=_blank
, jadi saya harus menyuntikkannya sendiri setelah diproses; - kode (apa pun yang diawali dengan empat spasi) mewarisi pembungkus
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(); } } } } } }
Ya, itu sekelompok kode yang jelek, tetapi akhirnya membawa saya ke set keluaran yang tidak akan membuat phpMyAdmin muntah, dan WordPress itu akan tampil dengan baik (cukup). Saya cukup memanggil program C# beberapa kali dengan rentang parameter yang berbeda:
AnswerHubMigrator 1 750 AnswerHubMigrator 751 1500 AnswerHubMigrator 1501 2250 ...
Kemudian saya membuka setiap file, menempelkannya ke phpMyAdmin, dan menekan GO:
phpMyAdmin (klik untuk memperbesar)Tentu saja saya harus menambahkan beberapa CSS di dalam WordPress untuk membantu membedakan antara pertanyaan, komentar, dan jawaban, dan juga untuk membuat indentasi komentar untuk menunjukkan balasan atas pertanyaan dan jawaban, komentar bersarang yang membalas komentar, dan seterusnya. Berikut adalah cuplikan saat Anda menelusuri pertanyaan selama sebulan:
Ubin pertanyaan (klik untuk memperbesar)Dan kemudian contoh posting, menampilkan gambar yang disematkan, beberapa lampiran, komentar bersarang, dan jawaban:
Contoh Pertanyaan dan Jawaban (klik untuk menuju ke sana)Saya masih mencoba memulihkan beberapa posting yang dikirimkan ke situs setelah pencadangan terakhir dilakukan, tetapi saya menyambut Anda untuk menelusurinya. Beri tahu kami jika Anda menemukan sesuatu yang hilang atau tidak pada tempatnya, atau bahkan hanya untuk memberi tahu kami bahwa konten tersebut masih berguna bagi Anda. Kami berharap dapat memperkenalkan kembali fungsionalitas unggahan paket dari dalam Plan Explorer, tetapi ini akan memerlukan beberapa pekerjaan API di situs dukungan baru, jadi saya tidak memiliki ETA untuk Anda hari ini.
- Answers.SQLPerformance.com