SELECT وINSERT على جدول في Google BigQuery، بما في ذلك مجموعات البيانات العامة. يُستنتج مخطط الجدول تلقائيًا من مخطط جدول BigQuery.
تتم القراءة باستخدام واجهة BigQuery REST API (tabledata.list)، لذا لا يمكن قراءة سوى الجداول الأصلية (ولا يمكن قراءة العروض أو العروض المادية أو الجداول الخارجية). تتم الكتابة باستخدام عمليات الإدراج المتدفقة (tabledata.insertAll)، والتي تتطلب تفعيل الفوترة للمشروع.
البنية النحوية
وسيطات الدالة
يمكن أيضًا تحديد وسيطات الدالة
project وdataset وtable وaccess_token بصيغة key = value؛ وتملأ وسيطات الدالة الموضعية هذه الخانات بالترتيب المذكور. ويُعد تحديد وسيط دالة موضعيًا وبصفته مفتاحًا في الوقت نفسه، أو تحديد المفتاح نفسه مرتين، خطأً.
يمكن تحديد وسيطات الدالة التالية بصيغة key = value (أو كمفاتيح في مجموعة مسماة):
المصادقة
- رمز الوصول. أي رمز وصول صالح وفق OAuth 2.0، مثل الرمز الناتج عن
gcloud auth print-access-token. تنتهي صلاحية الرموز سريعًا (عادةً بعد ساعة واحدة)، لذا تناسب هذه الطريقة الاستخدام التفاعلي. - مفتاح حساب الخدمة (موصى به للخوادم). مرّر محتوى ملف مفتاح أُنشئ في Google Cloud IAM باستخدام وسيطة الدالة
service_account_key. يوقّع ClickHouse رمز JWT بالمفتاح ويستبدله برمز وصول، ويجدده تلقائيًا. - رمز التحديث. مرّر
client_idوclient_secretوrefresh_token، مثل القيم المأخوذة من~/.config/gcloud/application_default_credentials.jsonبعد تشغيلgcloud auth application-default login.
BigQuery أو CREATE TABLE ... AS bigquery(...)) كتبعية للمجموعة، لذا يُحظر DROP NAMED COLLECTION ما دام الجدول موجودًا.
تعيين أنواع البيانات
ملاحظات:
- لا يتضمن
DATETIMEفي BigQuery منطقة زمنية؛ لذا يُعيَّن إلىDateTime64(6, 'UTC')حتى لا تعتمد القيمة المعروضة على المنطقة الزمنية للخادم. - يُعيَّن
RECORDمن النوعNULLABLEإلىNullable(Tuple(...))، بحيث يُحفَظNULLللسجل بأكمله بدلاً من اختزاله إلىTupleمن القيم الافتراضية. تتحول المصفوفةNULL(أو الفارغة) إلى مصفوفة فارغة، إذ لا يمكن أن يكونArrayداخلNullableفي ClickHouse. لا يمكن لمصفوفة BigQuery أن تحتوي على عناصرNULL(ARRAY<T>مكافئ لـARRAY<T NOT NULL>)، لذا لا يكون نوع عنصر الحقلREPEATEDهوNullable(Array(T)، أوArray(Tuple(...))لعنصر من نوعRECORD)؛ ويُرفض عنصرNULLفي استجابةtabledata.listباعتباره إدخالاً غير صالح. - تعمل قراءة أعمدة
Nullable(Tuple(...))وكتابتها عبر دالة الجدولbigqueryدون إعدادات إضافية. يتطلب إنشاء جدول دائم بمحركBigQueryيحتوي على مثل هذا العمود، سواء استُنتج الهيكل أو صُرّح به صراحةً، الإعدادenable_nullable_tuple_type، كما هو الحال مع أي عمودNullable(Tuple). عند التصريح عن الأعمدة صراحةً، يمكن بدلاً من ذلك التصريح بحقلRECORDكـTuple(...)عادي لتجنب هذا الإعداد، لكن على حساب تحويلNULLللسجل بأكمله إلى tuple افتراضي؛ والاختلاف الوحيد المقبول عن النوع المستنتج هو إزالةNullableالذي يغلّفTupleالخاص بـRECORD، وفقط في ذلك السجل نفسه — لا يمكن نقل قابلية القيم الفارغة إلى سجل آخر، داخلياً أو خارجياً. - يُعيَّن
GEOGRAPHYإلى Geometry. ينقل BigQuery قيمةGEOGRAPHYكنص WKT، ويحللها إلى البديل المطابق منGeometry(وهوVariantمنPointوMultiPointوRingوLineStringوMultiLineStringوPolygonوMultiPolygon) عند القراءة، ثم يعيد تسلسلها إلى WKT عند الكتابة. لا يملكGEOMETRYCOLLECTIONوالشكل الهندسي الفارغ، مثلPOINT EMPTY، مقابلاً فيGeometry، لذا تؤدي قراءة صف يحتوي على إحدى هذه القيم إلى حدوث خطأ. وبما أنVariantيحتويNULLبذاته، يُعيَّن حقلGEOGRAPHYمن النوعNULLABLEإلىGeometryوليس إلىNullable(Geometry)، مع الحفاظ علىNULLعند النقل ذهاباً وإياباً. - يُعيَّن
JSONإلىStringبدلاً من نوع البيانات JSON، لأن نوعJSONفي ClickHouse لا يقبل في المستوى الأعلى إلا كائناً ({...})، بينما يمكن أن تكون قيمةJSONفي BigQuery أي قيمة JSON، مثل قيمة scalar أو مصفوفة أوnull، ولذلك لا يمكن قراءة جدول يحتوي على مثل هذه القيم. إضافةً إلى ذلك، لا يمكن تغليفJSONبـNullable، لذا لن يُحفَظ SQLNULLفي عمودNULLABLE. تعيينStringلا يفقد البيانات؛ ويمكن تحويل الكائنات في المستوى الأعلى باستخدامCAST(value AS JSON). - لا تتسع قيم
BIGNUMERICالتي يحتوي جزؤها الصحيح على أكثر من 38 رقماً ضمنDecimal(76, 38)، وتؤدي إلى حدوث خطأ. - قيم
TIMESTAMPوDATEالواقعة خارج نطاقDateTime64/Date32، أي السنوات 1900-2299، غير مدعومة. - أعمدة
RANGEللقراءة فقط. تتوقعtabledata.insertAllقيمةRANGE<T>على شكل كائن مهيكل{start, end}، ولا يمكن إعادة بنائها من تعيينString، لذا تؤدي عملية الإدراج في عمودRANGEإلى حدوث خطأ. - تُرسل قيم
INT64إلىtabledata.insertAllكسلاسل عشرية، لأن واجهة API تحلل أرقام JSON كقيم ذات دقة مزدوجة، وإلا فقد تتلف القيم الواقعة خارج[-2^53 + 1, 2^53 - 1].
أمثلة
gcloud:
القيود
- لا يمكن قراءة سوى جداول BigQuery الأصلية. تتطلب طرق العرض والجداول الخارجية تشغيل مهمة استعلام في BigQuery، وهو ما لا تقوم به هذه الدالة.
- يمكن قراءة أعمدة
RANGE(بصفتهاString) ولكن لا يمكن الكتابة إليها: إذ يؤدي الإدراج في عمودRANGEإلى حدوث خطأ. - لا يمكن تمثيل قيمة
GEOGRAPHYالتي تكونGEOMETRYCOLLECTIONأو شكلًا هندسيًا فارغًا بالنوعGeometry، لذا تؤدي قراءة صف يحتوي على أي منهما إلى حدوث خطأ. تُرفض كتابةGeometryبقيمةNULLفي حقلGEOGRAPHYمن نوعREQUIRED، أو كعنصر في حقلGEOGRAPHYمن نوعREPEATED، لأن BigQuery لا يقبل قيمNULLفي تلك الحالات. - لا تُدفع شروط التصفية إلى المصدر: إذ إن
tabledata.listلا تسرد سوى صفوف الجدول ولا تحتوي على أي معلمة للتصفية (بل تقبل خيارات ترقيم الصفحات واختيار الأعمدة والتنسيق)، كما أن التصفية تتطلب تشغيل مهمة استعلام في BigQuery، وهو ما لا تقوم به هذه الدالة. لذلك، يُطبَّق شرطWHEREفي ClickHouse بعد تنزيل الصفوف؛ استخدم اختيار الأعمدة لتقليل البيانات المنقولة. - في المقابل، يقلل
LIMITمقدار البيانات المقروءة. تُطلب الصفحات عند الحاجة، مع تعيينmaxResultsإلىmax_block_size، ولا تُطلب صفحات إضافية بمجرد أن يحصل الاستعلام على عدد كافٍ من الصفوف. بالنسبة إلىLIMIT nبسيط (من دونWHEREأوGROUP BYأوORDER BY، ومع كونnأقل منmax_block_size)، يخفض ClickHouse قيمةmax_block_sizeإلىn، لذا يُجرى طلب واحد فقط للحصول علىnصفوف بالضبط؛ وإلا، تتوقف القراءة عند أول حد للصفحة بعد الحد، مع تجاوز بأقل من صفحة واحدة. - تُثبَّت القراءة على المخطط الذي تمت رؤيته وقت تحليل الاستعلام، عبر تمرير القائمة الصريحة للأعمدة إلى
tabledata.list. بالنسبة إلى قراءة واسعة جدًا تتجاوز فيها قائمة الأعمدة حد طول URL للطلب (مثلSELECT *من جدول يحوي آلاف الأعمدة)، يُرفض الاستعلام بدلًا من القراءة دون تثبيت، لأن القراءة غير المثبتة قد تنحرف بسبب تغيير متزامن في المخطط؛ اختر أعمدة أقل كي تتسع القائمة. ويُتحقق من حد طول URL نفسه قبل كل طلب لصفحة مرقمة (إذ تحمل كل صفحةpageTokenمعتمًا)، لذا تُرفض القراءة التي لا تتسع صفحاتها اللاحقة ضمن الحد بالخطأ نفسه بدلًا من أن تفشل جزئيًا. - إذا عُدّل جدول BigQuery بعد قراءة مخططه، يُرفض الاستعلام بدلًا من إرجاع بيانات غير متطابقة أو كتابتها بصمت: يُجلب المخطط الحالي مجددًا ويُقارن بالمخطط الذي حُلّل مباشرةً قبل القراءة، ومرة أخرى قبل أن يبث
INSERTصفه الأول. ولا يمكن سد النافذة المتبقية، أي تغيير المخطط بين ذلك الفحص والطلبات اللاحقة، لأن المخطط والبيانات يُجلبان عبر طلبات REST منفصلة. - تُجرى المقارنة مع لقطة المخطط التي حُلّل الاستعلام باستخدامها، وتُلتقط عند تحديد دالة الجدول لبنيتها أو، بالنسبة إلى جدول دائم (جدول بمحرك
BigQuery، أو جدول أُنشئ باستخدامCREATE TABLE ... AS bigquery(...)ويحتفظ بأعمدته بالطريقة نفسها)، عند أول قراءة أو كتابة له بعدCREATEأوATTACHأو إعادة تشغيل الخادم. تحتفظ بيانات تعريف الجدول بأعمدة ClickHouse المُعيَّنة، لا بمخطط BigQuery، لذا يُعتمد تغيير المخطط الذي أُجري أثناء فصل الجدول أو توقف الخادم عند الاستعلام التالي بدلًا من رفضه: إذ تظل الأعمدة المعلنة خاضعة للتحقق مقابل المخطط الحالي، وتُفك شفرة الصفوف وفقًا له، لذلك يُقرأ تغيير يحافظ على أنواع ClickHouse المُعيَّنة (مثلًا منSTRINGإلىBYTES) وفق قواعد النوع الجديد مع الإبقاء على نوع العمود نفسه. - تصل الصفوف المكتوبة باستخدام عمليات الإدراج المتدفقة إلى المخزن المؤقت للتدفق في BigQuery، وقد تستغرق بعض الوقت قبل أن تصبح مرئية للقراءات اللاحقة.
- يُرسل
INSERTكبير إلىtabledata.insertAllعلى دفعات: بحد أقصى 500 صف لكل طلب، ويُقسّم أيضًا بحيث يبقى كل طلب دون حد BigQuery البالغ 10 ميغابايت لحجم الطلب (ويُرفض الصف الواحد الذي يتجاوز هذا الحد برسالة خطأ واضحة). - عمليات الكتابة ليست ذرّية، وقد ينجح طلب واحد من
tabledata.insertAllجزئيًا: إذ يمكن لـ BigQuery تثبيت بعض صفوف الطلب ورفض الصفوف الأخرى معinsertErrors. كذلك، تُثبَّت الطلبات بصورة مستقلة عن بعضها، لذا قد تُرفض دفعة لاحقة بعد قبول دفعات سابقة. في كلتا الحالتين، يُبلغ الاستعلام عن خطأ، لكن الصفوف المُثبَّتة بالفعل تبقى في BigQuery. للحد من التكرار، يُرسَل كل صف معinsertIdثابت مشتق من معرّف الاستعلام وموضعه الترتيبي في الدفق، ويستخدمه BigQuery لإزالة التكرارات بأفضل جهد ضمن نافذة الإدراج المتدفق. إذا تجاوزquery_idحد BigQuery البالغ 128 حرفًا لـinsertId، يُجزَّأ إلى بادئة ثابتة الطول تظل ثابتة لهذاquery_id. ولأنinsertIdيعتمد على الموضع الترتيبي، لا تكون إزالة التكرارات موثوقة إلا إذا أنتجت إعادة التشغيل الصفوف بالترتيب نفسه: إعادة محاولة دفعة على مستوى النقل آمنة دائمًا، وإعادة تشغيلINSERTنفسه باستخدامquery_idنفسه لا تزيل التكرارات إلا إذا قدّمت الصفوف بالترتيب نفسه (مثل إدراج أحادي الخيط، أو ترتيب حتمي بطريقة أخرى — اضبطmax_threads = 1وmax_insert_threads = 1لعمليةINSERT ... SELECTمتوازية قد يتغير فيها ترتيب المقاطع بين المحاولات).