> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-mintlify-8c05c8a2.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> تحليل بيانات Stack Overflow باستخدام ClickHouse

# تحليل بيانات Stack Overflow باستخدام ClickHouse

export const Image = ({img, alt, size}) => {
  return <Frame>
      <img src={img} alt={alt} />
    </Frame>;
};

تحتوي مجموعة البيانات هذه على جميع عناصر `Posts` و`Users` و`Votes` و`Comments` و`Badges` و`PostHistory` و`PostLinks` الموجودة على Stack Overflow.

يمكنك إما تنزيل إصدارات Parquet المُعدّة مسبقًا من هذه البيانات، والتي تتضمن جميع المنشورات حتى أبريل 2024، أو تنزيل أحدث البيانات بتنسيق XML ثم تحميلها. يوفّر Stack Overflow تحديثات لهذه البيانات بشكل دوري - وكان ذلك تاريخيًا كل 3 أشهر.

يوضح المخطط التالي بنية الجداول المتاحة بافتراض استخدام تنسيق Parquet.

<Image img="https://mintcdn.com/private-7c7dfe99-mintlify-8c05c8a2/51i4N09qYnRiav8Z/images/getting-started/example-datasets/stackoverflow.png?fit=max&auto=format&n=51i4N09qYnRiav8Z&q=85&s=13459e34cd4e0272c83b7d86aeb281fe" alt="بنية Stack Overflow" size="md" width="1268" height="856" data-path="images/getting-started/example-datasets/stackoverflow.png" />

يمكن العثور على وصف لبنية هذه البيانات [هنا](https://meta.stackexchange.com/questions/2677/database-schema-documentation-for-the-public-data-dump-and-sede).

<div id="pre-prepared-data">
  ## بيانات مُعدّة مسبقًا
</div>

نوفّر نسخة من هذه البيانات بتنسيق Parquet، ومحدَّثة حتى أبريل 2024. وعلى الرغم من أن مجموعة البيانات هذه تُعد صغيرة بالنسبة إلى ClickHouse من حيث عدد الصفوف (60 مليون منشور)، فإنها تحتوي على كميات كبيرة من النصوص وأعمدة String كبيرة.

```sql theme={null}
CREATE DATABASE stackoverflow
```

المدد الزمنية التالية خاصة بعنقود ClickHouse Cloud بسعة 96 GiB و24 vCPU والموجود في `eu-west-2`. وتقع مجموعة البيانات في `eu-west-3`.

<div id="posts">
  ### Posts
</div>

```sql theme={null}
CREATE TABLE stackoverflow.posts
(
    `Id` Int32 CODEC(Delta(4), ZSTD(1)),
    `PostTypeId` Enum8('Question' = 1, 'Answer' = 2, 'Wiki' = 3, 'TagWikiExcerpt' = 4, 'TagWiki' = 5, 'ModeratorNomination' = 6, 'WikiPlaceholder' = 7, 'PrivilegeWiki' = 8),
    `AcceptedAnswerId` UInt32,
    `CreationDate` DateTime64(3, 'UTC'),
    `Score` Int32,
    `ViewCount` UInt32 CODEC(Delta(4), ZSTD(1)),
    `Body` String,
    `OwnerUserId` Int32,
    `OwnerDisplayName` String,
    `LastEditorUserId` Int32,
    `LastEditorDisplayName` String,
    `LastEditDate` DateTime64(3, 'UTC') CODEC(Delta(8), ZSTD(1)),
    `LastActivityDate` DateTime64(3, 'UTC'),
    `Title` String,
    `Tags` String,
    `AnswerCount` UInt16 CODEC(Delta(2), ZSTD(1)),
    `CommentCount` UInt8,
    `FavoriteCount` UInt8,
    `ContentLicense` LowCardinality(String),
    `ParentId` String,
    `CommunityOwnedDate` DateTime64(3, 'UTC'),
    `ClosedDate` DateTime64(3, 'UTC')
)
ENGINE = MergeTree
PARTITION BY toYear(CreationDate)
ORDER BY (PostTypeId, toDate(CreationDate), CreationDate)

INSERT INTO stackoverflow.posts SELECT * FROM s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/stackoverflow/parquet/posts/*.parquet')
```

```response theme={null}
0 rows in set. Elapsed: 265.466 sec. Processed 59.82 million rows, 38.07 GB (225.34 thousand rows/s., 143.42 MB/s.)
```

يتوفر جدول Posts أيضًا مقسمًا حسب السنة، مثل: [https://datasets-documentation.s3.eu-west-3.amazonaws.com/stackoverflow/parquet/posts/2020.parquet](https://datasets-documentation.s3.eu-west-3.amazonaws.com/stackoverflow/parquet/posts/2020.parquet)

<div id="votes">
  ### Votes
</div>

```sql theme={null}
CREATE TABLE stackoverflow.votes
(
    `Id` UInt32,
    `PostId` Int32,
    `VoteTypeId` UInt8,
    `CreationDate` DateTime64(3, 'UTC'),
    `UserId` Int32,
    `BountyAmount` UInt8
)
ENGINE = MergeTree
ORDER BY (VoteTypeId, CreationDate, PostId, UserId)

INSERT INTO stackoverflow.votes SELECT * FROM s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/stackoverflow/parquet/votes/*.parquet')
```

```response theme={null}
0 rows in set. Elapsed: 21.605 sec. Processed 238.98 million rows, 2.13 GB (11.06 million rows/s., 98.46 MB/s.)
```

تتوفر بيانات Votes أيضًا حسب السنة، مثل [https://datasets-documentation.s3.eu-west-3.amazonaws.com/stackoverflow/parquet/votes/2020.parquet](https://datasets-documentation.s3.eu-west-3.amazonaws.com/stackoverflow/parquet/votes/2020.parquet)

<div id="comments">
  ### Comments
</div>

```sql theme={null}
CREATE TABLE stackoverflow.comments
(
    `Id` UInt32,
    `PostId` UInt32,
    `Score` UInt16,
    `Text` String,
    `CreationDate` DateTime64(3, 'UTC'),
    `UserId` Int32,
    `UserDisplayName` LowCardinality(String)
)
ENGINE = MergeTree
ORDER BY CreationDate

INSERT INTO stackoverflow.comments SELECT * FROM s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/stackoverflow/parquet/comments/*.parquet')
```

```response theme={null}
0 rows in set. Elapsed: 56.593 sec. Processed 90.38 million rows, 11.14 GB (1.60 million rows/s., 196.78 MB/s.)
```

تتوفر بيانات Comments أيضًا حسب السنة، على سبيل المثال [https://datasets-documentation.s3.eu-west-3.amazonaws.com/stackoverflow/parquet/comments/2020.parquet](https://datasets-documentation.s3.eu-west-3.amazonaws.com/stackoverflow/parquet/comments/2020.parquet)

<div id="users">
  ### Users
</div>

```sql theme={null}
CREATE TABLE stackoverflow.users
(
    `Id` Int32,
    `Reputation` LowCardinality(String),
    `CreationDate` DateTime64(3, 'UTC') CODEC(Delta(8), ZSTD(1)),
    `DisplayName` String,
    `LastAccessDate` DateTime64(3, 'UTC'),
    `AboutMe` String,
    `Views` UInt32,
    `UpVotes` UInt32,
    `DownVotes` UInt32,
    `WebsiteUrl` String,
    `Location` LowCardinality(String),
    `AccountId` Int32
)
ENGINE = MergeTree
ORDER BY (Id, CreationDate)

INSERT INTO stackoverflow.users SELECT * FROM s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/stackoverflow/parquet/users.parquet')
```

```response theme={null}
0 rows in set. Elapsed: 10.988 sec. Processed 22.48 million rows, 1.36 GB (2.05 million rows/s., 124.10 MB/s.)
```

<div id="badges">
  ### Badges
</div>

```sql theme={null}
CREATE TABLE stackoverflow.badges
(
    `Id` UInt32,
    `UserId` Int32,
    `Name` LowCardinality(String),
    `Date` DateTime64(3, 'UTC'),
    `Class` Enum8('Gold' = 1, 'Silver' = 2, 'Bronze' = 3),
    `TagBased` Bool
)
ENGINE = MergeTree
ORDER BY UserId

INSERT INTO stackoverflow.badges SELECT * FROM s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/stackoverflow/parquet/badges.parquet')
```

```response theme={null}
0 rows in set. Elapsed: 6.635 sec. Processed 51.29 million rows, 797.05 MB (7.73 million rows/s., 120.13 MB/s.)
```

<div id="postlinks">
  ### PostLinks
</div>

```sql theme={null}
CREATE TABLE stackoverflow.postlinks
(
    `Id` UInt64,
    `CreationDate` DateTime64(3, 'UTC'),
    `PostId` Int32,
    `RelatedPostId` Int32,
    `LinkTypeId` Enum8('Linked' = 1, 'Duplicate' = 3)
)
ENGINE = MergeTree
ORDER BY (PostId, RelatedPostId)

INSERT INTO stackoverflow.postlinks SELECT * FROM s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/stackoverflow/parquet/postlinks.parquet')
```

```response theme={null}
0 rows in set. Elapsed: 1.534 sec. Processed 6.55 million rows, 129.70 MB (4.27 million rows/s., 84.57 MB/s.)
```

<div id="posthistory">
  ### PostHistory
</div>

```sql theme={null}
CREATE TABLE stackoverflow.posthistory
(
    `Id` UInt64,
    `PostHistoryTypeId` UInt8,
    `PostId` Int32,
    `RevisionGUID` String,
    `CreationDate` DateTime64(3, 'UTC'),
    `UserId` Int32,
    `Text` String,
    `ContentLicense` LowCardinality(String),
    `Comment` String,
    `UserDisplayName` String
)
ENGINE = MergeTree
ORDER BY (CreationDate, PostId)

INSERT INTO stackoverflow.posthistory SELECT * FROM s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/stackoverflow/parquet/posthistory/*.parquet')
```

```response theme={null}
0 rows in set. Elapsed: 422.795 sec. Processed 160.79 million rows, 67.08 GB (380.30 thousand rows/s., 158.67 MB/s.)
```

<div id="original-dataset">
  ## مجموعة البيانات الأصلية
</div>

تتوفّر مجموعة البيانات الأصلية بصيغة XML مضغوطة (7zip) على [https://archive.org/download/stackexchange](https://archive.org/download/stackexchange) - الملفات ذات البادئة `stackoverflow.com*`.

<div id="download">
  ### تنزيل
</div>

```bash theme={null}
wget https://archive.org/download/stackexchange/stackoverflow.com-Badges.7z
wget https://archive.org/download/stackexchange/stackoverflow.com-Comments.7z
wget https://archive.org/download/stackexchange/stackoverflow.com-PostHistory.7z
wget https://archive.org/download/stackexchange/stackoverflow.com-PostLinks.7z
wget https://archive.org/download/stackexchange/stackoverflow.com-Posts.7z
wget https://archive.org/download/stackexchange/stackoverflow.com-Users.7z
wget https://archive.org/download/stackexchange/stackoverflow.com-Votes.7z
```

تصل سعة هذه الملفات إلى 35 غيغابايت، وقد يستغرق تنزيلها حوالي 30 دقيقة تبعًا لاتصال الإنترنت، إذ يحدّ خادم التنزيل السرعة عند نحو 20 ميغابايت/ثانية.

<div id="convert-to-json">
  ### التحويل إلى JSON
</div>

حتى وقت كتابة هذا النص، لا يوفّر ClickHouse دعمًا أصليًا لـ XML كصيغة إدخال. ولتحميل البيانات إلى ClickHouse، نحوّلها أولًا إلى NDJSON.

لتحويل XML إلى JSON، نوصي باستخدام أداة [`xq`](https://github.com/kislyuk/yq) على Linux، وهي `wrapper` بسيطة لأداة `jq` للتعامل مع مستندات XML.

ثبّت xq و jq:

```bash theme={null}
sudo apt install jq
pip install yq
```

تنطبق الخطوات التالية على أيٍّ من الملفات المذكورة أعلاه. نستخدم الملف `stackoverflow.com-Posts.7z` كمثال. عدِّل حسب الحاجة.

استخرج الملف باستخدام [p7zip](https://p7zip.sourceforge.net/). سينتج عن ذلك ملف XML واحد — في هذه الحالة `Posts.xml`.

> الملفات مضغوطة بحوالي 4.5×. وعند حجم مضغوط قدره 22GB، يتطلب ملف منشور نحو 97G بدون ضغط.

```bash theme={null}
p7zip -d stackoverflow.com-Posts.7z
```

يقسّم ما يلي ملف XML إلى عدة ملفات، يحتوي كلٌّ منها على 10000 صف.

```bash theme={null}
mkdir posts
cd posts
# the following splits the input xml file into sub files of 10000 rows
tail +3 ../Posts.xml | head -n -1 | split -l 10000 --filter='{ printf "<rows>\n"; cat - ; printf "</rows>\n"; } > $FILE' -
```

بعد تشغيل ما سبق، ستكون لديك مجموعة من الملفات، يحتوي كلٌّ منها على 10000 سطر. يضمن ذلك ألّا يكون استهلاك الذاكرة الإضافي للأمر التالي كبيرًا جدًا (يُجرى تحويل xml إلى JSON في الذاكرة).

```bash theme={null}
find . -maxdepth 1 -type f -exec xq -c '.rows.row[]' {} \; | sed -e 's:"@:":g' > posts_v2.json
```

سينتج عن الأمر أعلاه ملف واحد باسم `posts.json`.

حمِّل الملف إلى ClickHouse باستخدام الأمر التالي. لاحظ أنه تم تحديد البنية لملف `posts.json`. وستحتاج إلى تعديل ذلك بحسب نوع البيانات ليتوافق مع الجدول الهدف.

```bash theme={null}
clickhouse local --query "SELECT * FROM file('posts.json', JSONEachRow, 'Id Int32, PostTypeId UInt8, AcceptedAnswerId UInt32, CreationDate DateTime64(3, \'UTC\'), Score Int32, ViewCount UInt32, Body String, OwnerUserId Int32, OwnerDisplayName String, LastEditorUserId Int32, LastEditorDisplayName String, LastEditDate DateTime64(3, \'UTC\'), LastActivityDate DateTime64(3, \'UTC\'), Title String, Tags String, AnswerCount UInt16, CommentCount UInt8, FavoriteCount UInt8, ContentLicense String, ParentId String, CommunityOwnedDate DateTime64(3, \'UTC\'), ClosedDate DateTime64(3, \'UTC\')') FORMAT Native" | clickhouse client --host <host> --secure --password <password> --query "INSERT INTO stackoverflow.posts_v2 FORMAT Native"
```

<div id="example-queries">
  ## أمثلة على الاستعلامات
</div>

بعض الأسئلة البسيطة لمساعدتك على البدء.

<div id="most-popular-tags-on-stack-overflow">
  ### أكثر الوسوم شيوعًا على Stack Overflow
</div>

```sql theme={null}
SELECT
    arrayJoin(arrayFilter(t -> (t != ''), splitByChar('|', Tags))) AS Tags,
    count() AS c
FROM stackoverflow.posts
GROUP BY Tags
ORDER BY c DESC
LIMIT 10
```

```response theme={null}
┌─Tags───────┬───────c─┐
│ javascript │ 2527130 │
│ python     │ 2189638 │
│ java       │ 1916156 │
│ c#         │ 1614236 │
│ php        │ 1463901 │
│ android    │ 1416442 │
│ html       │ 1186567 │
│ jquery     │ 1034621 │
│ c++        │  806202 │
│ css        │  803755 │
└────────────┴─────────┘

10 rows in set. Elapsed: 1.013 sec. Processed 59.82 million rows, 1.21 GB (59.07 million rows/s., 1.19 GB/s.)
Peak memory usage: 224.03 MiB.
```

<div id="user-with-the-most-answers-active-accounts">
  ### المستخدم صاحب أكبر عدد من الإجابات (الحسابات النشطة)
</div>

يتطلب الحساب `UserId`.

```sql theme={null}
SELECT
    any(OwnerUserId) UserId,
    OwnerDisplayName,
    count() AS c
FROM stackoverflow.posts WHERE OwnerDisplayName != '' AND PostTypeId='Answer' AND OwnerUserId != 0
GROUP BY OwnerDisplayName
ORDER BY c DESC
LIMIT 5
```

```response theme={null}
┌─UserId─┬─OwnerDisplayName─┬────c─┐
│  22656 │ Jon Skeet        │ 2727 │
│  23354 │ Marc Gravell     │ 2150 │
│  12950 │ tvanfosson       │ 1530 │
│   3043 │ Joel Coehoorn    │ 1438 │
│  10661 │ S.Lott           │ 1087 │
└────────┴──────────────────┴──────┘

5 rows in set. Elapsed: 0.154 sec. Processed 35.83 million rows, 193.39 MB (232.33 million rows/s., 1.25 GB/s.)
Peak memory usage: 206.45 MiB.
```

<div id="clickhouse-related-posts-with-the-most-views">
  ### المنشورات المتعلقة بـ ClickHouse الأكثر مشاهدة
</div>

```sql theme={null}
SELECT
    Id,
    Title,
    ViewCount,
    AnswerCount
FROM stackoverflow.posts
WHERE Title ILIKE '%ClickHouse%'
ORDER BY ViewCount DESC
LIMIT 10
```

```response theme={null}
┌───────Id─┬─Title────────────────────────────────────────────────────────────────────────────┬─ViewCount─┬─AnswerCount─┐
│ 52355143 │ Is it possible to delete old records from clickhouse table?                      │     41462 │           3 │
│ 37954203 │ Clickhouse Data Import                                                           │     38735 │           3 │
│ 37901642 │ Updating data in Clickhouse                                                      │     36236 │           6 │
│ 58422110 │ Pandas: How to insert dataframe into Clickhouse                                  │     29731 │           4 │
│ 63621318 │ DBeaver - Clickhouse - SQL Error [159] .. Read timed out                         │     27350 │           1 │
│ 47591813 │ How to filter clickhouse table by array column contents?                         │     27078 │           2 │
│ 58728436 │ How to search the string in query with case insensitive on Clickhouse database?  │     26567 │           3 │
│ 65316905 │ Clickhouse: DB::Exception: Memory limit (for query) exceeded                     │     24899 │           2 │
│ 49944865 │ How to add a column in clickhouse                                                │     24424 │           1 │
│ 59712399 │ How to cast date Strings to DateTime format with extended parsing in ClickHouse? │     22620 │           1 │
└──────────┴──────────────────────────────────────────────────────────────────────────────────┴───────────┴─────────────┘

10 rows in set. Elapsed: 0.472 sec. Processed 59.82 million rows, 1.91 GB (126.63 million rows/s., 4.03 GB/s.)
Peak memory usage: 240.01 MiB.
```

<div id="most-controversial-posts">
  ### أكثر المنشورات إثارةً للجدل
</div>

```sql theme={null}
SELECT
    Id,
    Title,
    UpVotes,
    DownVotes,
    abs(UpVotes - DownVotes) AS Controversial_ratio
FROM stackoverflow.posts
INNER JOIN
(
    SELECT
        PostId,
        countIf(VoteTypeId = 2) AS UpVotes,
        countIf(VoteTypeId = 3) AS DownVotes
    FROM stackoverflow.votes
    GROUP BY PostId
    HAVING (UpVotes > 10) AND (DownVotes > 10)
) AS votes ON posts.Id = votes.PostId
WHERE Title != ''
ORDER BY Controversial_ratio ASC
LIMIT 3
```

```response theme={null}
┌───────Id─┬─Title─────────────────────────────────────────────┬─UpVotes─┬─DownVotes─┬─Controversial_ratio─┐
│   583177 │ VB.NET Infinite For Loop                          │      12 │        12 │                   0 │
│  9756797 │ Read console input as enumerable - one statement? │      16 │        16 │                   0 │
│ 13329132 │ What's the point of ARGV in Ruby?                 │      22 │        22 │                   0 │
└──────────┴───────────────────────────────────────────────────┴─────────┴───────────┴─────────────────────┘

3 rows in set. Elapsed: 4.779 sec. Processed 298.80 million rows, 3.16 GB (62.52 million rows/s., 661.05 MB/s.)
Peak memory usage: 6.05 GiB.
```

<div id="attribution">
  ## الإسناد
</div>

نشكر Stack Overflow على إتاحة هذه البيانات بموجب ترخيص `cc-by-sa 4.0`، مع الإشارة إلى جهودهم وإلى المصدر الأصلي للبيانات على [https://archive.org/details/stackexchange](https://archive.org/details/stackexchange).
