SQL อะไรจะง่ายกว่านี้? เราแต่ละคนสามารถเขียนคำของ่ายๆ - เราพิมพ์ เลือกจากนั้นแสดงรายการคอลัมน์ที่ต้องการ ราคาเริ่มต้นที่, ชื่อตาราง, เงื่อนไขบางประการใน ที่ไหน และนั่นคือทั้งหมด - ข้อมูลที่เป็นประโยชน์อยู่ในกระเป๋าของเราและ (เกือบ) ไม่ว่า DBMS ใดจะอยู่ภายใต้ประทุนในเวลานั้น (หรืออาจจะ)
และมาเริ่มต้นกันตั้งแต่
การทำแผนที่เชิงวัตถุสัมพันธ์
ผู้สนับสนุน ORM มักจะให้ความสำคัญกับความเร็วและความง่ายในการพัฒนา ความเป็นอิสระจาก DBMS และโค้ดที่สะอาด สำหรับพวกเราหลายคน รหัสสำหรับการทำงานกับฐานข้อมูล (และมักจะเป็นฐานข้อมูลด้วย)
มันมักจะมีลักษณะเช่นนี้...
@Entity
@Table(name = "stock", catalog = "maindb", uniqueConstraints = {
@UniqueConstraint(columnNames = "STOCK_NAME"),
@UniqueConstraint(columnNames = "STOCK_CODE") })
public class Stock implements java.io.Serializable {
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "STOCK_ID", unique = true, nullable = false)
public Integer getStockId() {
return this.stockId;
}
...
โมเดลนี้เต็มไปด้วยคำอธิบายประกอบที่ชาญฉลาด และในเบื้องหลัง ORM ผู้กล้าหาญจะสร้างและรันโค้ด SQL จำนวนมาก อย่างไรก็ตาม นักพัฒนาพยายามอย่างเต็มที่ที่จะแยกตัวเองออกจากฐานข้อมูลด้วยนามธรรมหลายกิโลเมตร ซึ่งบ่งบอกถึงบางอย่าง
ในอีกด้านหนึ่งของเครื่องกีดขวาง กลุ่มผู้ใช้ SQL “ที่ทำด้วยมือ” ล้วนๆ สังเกตความสามารถในการบีบน้ำทั้งหมดออกจาก DBMS ของตนโดยไม่ต้องมีเลเยอร์และนามธรรมเพิ่มเติม เป็นผลให้โครงการ "เน้นข้อมูลเป็นศูนย์กลาง" ปรากฏขึ้นโดยผู้ที่ได้รับการฝึกอบรมมาเป็นพิเศษมีส่วนร่วมในฐานข้อมูล (พวกเขายังเป็น "ผู้พื้นฐาน" พวกเขายังเป็น "ผู้พื้นฐาน" พวกเขายังเป็น "ผู้วางรากฐาน" ฯลฯ ) และนักพัฒนา เพียงแต่ต้อง “ดึง” มุมมองสำเร็จรูปและขั้นตอนการจัดเก็บโดยไม่ต้องลงรายละเอียด
จะเป็นอย่างไรถ้าเรามีสิ่งที่ดีที่สุดจากทั้งสองโลก? วิธีการทำสิ่งนี้ด้วยเครื่องมือที่ยอดเยี่ยมและมีชื่อที่ยืนยันถึงชีวิต
Clojure เป็นภาษาที่ยอดเยี่ยมสำหรับการสร้าง DSL แต่ SQL เองก็เป็น DSL ที่ยอดเยี่ยม และเราไม่ต้องการภาษาอื่นอีก นิพจน์ S นั้นยอดเยี่ยม แต่ไม่ได้เพิ่มอะไรใหม่ที่นี่ เป็นผลให้เราได้วงเล็บเพื่อประโยชน์ของวงเล็บ ไม่เห็นด้วย? จากนั้นรอสักครู่เมื่อสิ่งที่เป็นนามธรรมเหนือฐานข้อมูลเริ่มรั่วไหล และคุณเริ่มต่อสู้กับฟังก์ชัน (ดิบ-SQL)
แล้วฉันควรทำอย่างไร? ปล่อยให้ SQL เป็น SQL ปกติ - หนึ่งไฟล์ต่อคำขอ:
-- name: users-by-country
select *
from users
where country_code = :country_code
... จากนั้นอ่านไฟล์นี้ โดยเปลี่ยนให้เป็นฟังก์ชัน Clojure ปกติ:
(defqueries "some/where/users_by_country.sql"
{:connection db-spec})
;;; A function with the name `users-by-country` has been created.
;;; Let's use it:
(users-by-country {:country_code "GB"})
;=> ({:name "Kris" :country_code "GB" ...} ...)
เมื่อปฏิบัติตามหลักการ "SQL ด้วยตัวเอง Clojure ด้วยตัวเอง" คุณจะได้รับ:
- ไม่มีความประหลาดใจทางวากยสัมพันธ์ ฐานข้อมูลของคุณ (เช่นเดียวกับที่อื่น ๆ ) ไม่สอดคล้องกับมาตรฐาน SQL 100% - แต่สิ่งนี้ไม่สำคัญสำหรับ Yesql คุณจะไม่เสียเวลาไปกับการค้นหาฟังก์ชันที่มีไวยากรณ์เทียบเท่ากับ SQL คุณจะไม่ต้องกลับไปที่ฟังก์ชั่นอีกต่อไป (raw-sql "some('funky'::SYNTAX)")).
- การสนับสนุนบรรณาธิการที่ดีที่สุด โปรแกรมแก้ไขของคุณรองรับ SQL ที่ยอดเยี่ยมอยู่แล้ว ด้วยการบันทึก SQL เป็น SQL คุณสามารถใช้งานได้
- ความเข้ากันได้ของทีม DBA ของคุณสามารถอ่านและเขียน SQL ที่คุณใช้ในโครงการ Clojure ของคุณได้
- การปรับแต่งประสิทธิภาพที่ง่ายขึ้น ต้องการสร้างแผนสำหรับการสืบค้นที่มีปัญหาหรือไม่? นี่ไม่ใช่ปัญหาเมื่อแบบสอบถามของคุณเป็น SQL ปกติ
- การใช้แบบสอบถามซ้ำ ลากและวางไฟล์ SQL เดียวกันเหล่านั้นไปยังโปรเจ็กต์อื่น ๆ เนื่องจากเป็นเพียง SQL เก่าธรรมดา - เพียงแค่แชร์มัน
ในความคิดของฉัน แนวคิดนี้เจ๋งมากและในขณะเดียวกันก็เรียบง่ายมาก ซึ่งต้องขอบคุณโครงการที่ได้รับมากมาย
ผู้จัดการ IDE และ DB
เริ่มต้นด้วยงานง่ายๆ ในชีวิตประจำวัน บ่อยครั้งที่เราต้องค้นหาวัตถุบางอย่างในฐานข้อมูล เช่น ค้นหาตารางในสคีมาและศึกษาโครงสร้างของมัน (คอลัมน์ คีย์ ดัชนี ข้อจำกัด ฯลฯ ที่ใช้) และจาก IDE แบบกราฟิกหรือผู้จัดการ DB เพียงเล็กน้อย เราคาดหวังความสามารถเหล่านี้อย่างแน่นอน เพื่อให้รวดเร็วและคุณไม่ต้องรอครึ่งชั่วโมงจนกระทั่งหน้าต่างที่มีข้อมูลที่จำเป็นถูกดึงออกมา (โดยเฉพาะอย่างยิ่งกับการเชื่อมต่อที่ช้าไปยังฐานข้อมูลระยะไกล) และในขณะเดียวกัน ข้อมูลที่ได้รับก็จะใหม่และมีความเกี่ยวข้อง และไม่แคชขยะ นอกจากนี้ยิ่งฐานข้อมูลซับซ้อนและใหญ่ขึ้นและมีจำนวนฐานข้อมูลมากขึ้นเท่าใด การทำเช่นนี้ก็จะยิ่งยากขึ้นเท่านั้น
แต่โดยปกติแล้วฉันจะโยนเมาส์ทิ้งแล้วเขียนโค้ด สมมติว่าคุณต้องค้นหาว่าตารางใด (และมีคุณสมบัติใดบ้าง) ที่มีอยู่ในสคีมา "HR" ใน DBMS ส่วนใหญ่ ผลลัพธ์ที่ต้องการสามารถทำได้ด้วยการสืบค้นง่ายๆ จาก information_schema:
select table_name
, ...
from information_schema.tables
where schema = 'HR'
จากฐานข้อมูลหนึ่งไปอีกฐานข้อมูลหนึ่ง เนื้อหาของตารางอ้างอิงดังกล่าวจะแตกต่างกันไปขึ้นอยู่กับความสามารถของแต่ละ DBMS และตัวอย่างเช่น สำหรับ MySQL จากหนังสืออ้างอิงเดียวกัน คุณสามารถรับพารามิเตอร์ตารางเฉพาะสำหรับ DBMS นี้:
select table_name
, storage_engine -- Используемый "движок" ("MyISAM", "InnoDB" etc)
, row_format -- Формат строки ("Fixed", "Dynamic" etc)
, ...
from information_schema.tables
where schema = 'HR'
Oracle ไม่รู้จัก information_schema แต่มี
select table_name
, pct_free -- Минимум свободного места в блоке данных (%)
, pct_used -- Минимум используемого места в блоке данных (%)
, last_analyzed -- Дата последнего сбора статистики
, ...
from all_tables
where owner = 'HR'
ClickHouse ก็ไม่มีข้อยกเว้น:
select name
, engine -- Используемый "движок" ("MergeTree", "Dictionary" etc)
, ...
from system.tables
where database = 'HR'
สิ่งที่คล้ายกันสามารถทำได้ใน Cassandra (ซึ่งมี columnfamilies แทนที่จะเป็นตารางและ keyspaces แทนที่จะเป็น schema):
select columnfamily_name
, compaction_strategy_class -- Стратегия сборки мусора
, gc_grace_seconds -- Время жизни мусора
, ...
from system.schema_columnfamilies
where keyspace_name = 'HR'
สำหรับฐานข้อมูลอื่นๆ ส่วนใหญ่ คุณสามารถสร้างข้อความค้นหาที่คล้ายกันได้ (แม้แต่ Mongo ก็มี
แน่นอน ด้วยวิธีนี้ คุณสามารถรับข้อมูลไม่เพียงแต่เกี่ยวกับตาราง แต่ยังเกี่ยวกับวัตถุใดๆ โดยทั่วไปด้วย ในบางครั้งผู้ใจดีจะแบ่งปันรหัสดังกล่าวกับฐานข้อมูลต่าง ๆ เช่นในชุดบทความ Habra “ฟังก์ชั่นสำหรับจัดทำเอกสารฐานข้อมูล PostgreSQL” (
เป็นผลให้วิธีการนำทางและค้นหาวัตถุนี้มีความยืดหยุ่นมากขึ้นประหยัดเวลาได้มากและช่วยให้คุณได้รับข้อมูลในรูปแบบที่จำเป็นในขณะนี้ (ดังเช่นที่อธิบายไว้ในโพสต์
การดำเนินการกับวัตถุ
หลังจากที่เราค้นพบและศึกษาวัตถุที่จำเป็นแล้ว ก็ถึงเวลาทำสิ่งที่มีประโยชน์กับพวกมัน โดยธรรมชาติแล้วโดยไม่ต้องละนิ้วออกจากคีย์บอร์ด
เป็นความลับที่การลบตารางจะมีลักษณะเหมือนกันในฐานข้อมูลเกือบทั้งหมด:
drop table hr.persons
แต่ด้วยการสร้างโต๊ะมันจึงน่าสนใจยิ่งขึ้น DBMS เกือบทุกตัว (รวมถึง NoSQL จำนวนมาก) สามารถ "สร้างตาราง" ในรูปแบบใดรูปแบบหนึ่งได้ และส่วนหลักของตารางจะแตกต่างกันเล็กน้อยด้วยซ้ำ (ชื่อ รายการคอลัมน์ ประเภทข้อมูล) แต่รายละเอียดอื่น ๆ อาจแตกต่างกันอย่างมากและขึ้นอยู่กับ อุปกรณ์ภายในและความสามารถของ DBMS เฉพาะ ตัวอย่างที่ฉันชอบคือในเอกสารของ Oracle มีเพียง BNF แบบ "เปล่า" สำหรับไวยากรณ์ "สร้างตาราง"
นอกจากนี้ DBMS จำนวนมากยังมีออบเจ็กต์ประเภทเฉพาะของตนเองซึ่งไม่มีใน DBMS อื่น ยิ่งไปกว่านั้น เราสามารถดำเนินการได้ไม่เพียงแต่บนออบเจ็กต์ฐานข้อมูลเท่านั้น แต่ยังรวมถึง DBMS เองด้วย เช่น "ฆ่า" กระบวนการ เพิ่มพื้นที่หน่วยความจำบางส่วน เปิดใช้งานการติดตาม สลับไปที่โหมด "อ่านอย่างเดียว" และอื่นๆ อีกมากมาย
ทีนี้มาวาดกันหน่อย
งานทั่วไปอย่างหนึ่งคือการสร้างไดอะแกรมด้วยวัตถุฐานข้อมูลและดูวัตถุและการเชื่อมต่อระหว่างวัตถุเหล่านั้นในภาพที่สวยงาม IDE แบบกราฟิกเกือบทั้งหมด, ยูทิลิตี้ "บรรทัดคำสั่ง" ที่แยกจากกัน, เครื่องมือกราฟิกเฉพาะทางและผู้สร้างโมเดลสามารถทำได้ พวกเขาจะวาดบางสิ่งบางอย่างให้กับคุณ "ดีที่สุดเท่าที่จะทำได้" และคุณสามารถมีอิทธิพลต่อกระบวนการนี้เพียงเล็กน้อยเท่านั้นด้วยความช่วยเหลือของพารามิเตอร์บางตัวในไฟล์การกำหนดค่าหรือช่องทำเครื่องหมายในอินเทอร์เฟซ
แต่ปัญหานี้สามารถแก้ไขได้ง่ายกว่า ยืดหยุ่นกว่า และสวยงามกว่ามาก และแน่นอนด้วยความช่วยเหลือของโค้ด ในการสร้างไดอะแกรมของความซับซ้อนใด ๆ เรามีภาษามาร์กอัปเฉพาะหลายภาษา (DOT, GraphML ฯลฯ ) และสำหรับพวกเขาก็มีแอปพลิเคชันทั้งหมด (GraphViz, PlantUML, Mermaid) ที่สามารถอ่านคำแนะนำดังกล่าวและแสดงภาพในรูปแบบที่หลากหลาย . เรารู้วิธีรับข้อมูลเกี่ยวกับวัตถุและการเชื่อมต่อระหว่างวัตถุเหล่านั้นแล้ว
นี่คือตัวอย่างเล็ก ๆ ของลักษณะที่ปรากฏ โดยใช้ PlantUML และ
select '@startuml'||chr(10)||'hide methods'||chr(10)||'hide stereotypes' union all
select distinct ccu.table_name || ' --|> ' ||
tc.table_name as val
from table_constraints as tc
join key_column_usage as kcu
on tc.constraint_name = kcu.constraint_name
join constraint_column_usage as ccu
on ccu.constraint_name = tc.constraint_name
where tc.constraint_type = 'FOREIGN KEY'
and tc.table_name ~ '.*' union all
select '@enduml'
และถ้าคุณลองเพียงเล็กน้อยก็ขึ้นอยู่กับ
แบบสอบถาม SQL นั้นซับซ้อนกว่าเล็กน้อย
-- Шапка
select '@startuml
!define Table(name,desc) class name as "desc" << (T,#FFAAAA) >>
!define primary_key(x) <b>x</b>
!define unique(x) <color:green>x</color>
!define not_null(x) <u>x</u>
hide methods
hide stereotypes'
union all
-- Таблицы
select format('Table(%s, "%s n information about %s") {'||chr(10), table_name, table_name, table_name) ||
(select string_agg(column_name || ' ' || upper(udt_name), chr(10))
from information_schema.columns
where table_schema = 'public'
and table_name = t.table_name) || chr(10) || '}'
from information_schema.tables t
where table_schema = 'public'
union all
-- Связи между таблицами
select distinct ccu.table_name || ' "1" --> "0..N" ' || tc.table_name || format(' : "A %s may haven many %s"', ccu.table_name, tc.table_name)
from information_schema.table_constraints as tc
join information_schema.key_column_usage as kcu on tc.constraint_name = kcu.constraint_name
join information_schema.constraint_column_usage as ccu on ccu.constraint_name = tc.constraint_name
where tc.constraint_type = 'FOREIGN KEY'
and ccu.constraint_schema = 'public'
and tc.table_name ~ '.*'
union all
-- Подвал
select '@enduml'
หากคุณมองอย่างใกล้ชิด ภายใต้ประทุน เครื่องมือการแสดงภาพจำนวนมากยังใช้คำสั่งที่คล้ายกัน จริงอยู่ คำขอเหล่านี้มักจะเป็นเรื่องลึกซึ้ง
ตัวชี้วัดและการตรวจสอบ
เรามาดูหัวข้อที่ซับซ้อนแบบดั้งเดิมกันดีกว่า - การตรวจสอบประสิทธิภาพของฐานข้อมูล ฉันจำเรื่องจริงเล็กๆ น้อยๆ ที่ “เพื่อนคนหนึ่งของฉันเล่า” ให้ฉันฟังได้ ในอีกโครงการหนึ่งมี DBA ที่ทรงพลังอาศัยอยู่และมีนักพัฒนาเพียงไม่กี่คนที่รู้จักเขาเป็นการส่วนตัวหรือเคยเห็นเขาด้วยตัวเอง (แม้ว่าตามข่าวลือเขาจะทำงานที่ไหนสักแห่งในอาคารถัดไปก็ตาม) . ในชั่วโมง “X” เมื่อระบบ poduction ของผู้ค้าปลีกรายใหญ่เริ่ม “รู้สึกแย่” อีกครั้ง เขาได้ส่งภาพหน้าจอกราฟจาก Oracle Enterprise Manager อย่างเงียบๆ ซึ่งเขาเน้นจุดสำคัญอย่างระมัดระวังด้วยเครื่องหมายสีแดงเพื่อแสดง “ความเข้าใจ” ( พูดง่ายๆ ก็คือไม่ได้ช่วยอะไรมาก) และจาก "การ์ดรูปถ่าย" นี้ฉันต้องปฏิบัติต่อ ในเวลาเดียวกันไม่มีใครสามารถเข้าถึง Enterprise Manager อันล้ำค่า (ในทั้งสองความหมาย) เพราะ ระบบมีความซับซ้อนและมีราคาแพง ทันใดนั้น “นักพัฒนาก็สะดุดกับบางสิ่งบางอย่างและทำลายทุกอย่าง” ดังนั้นนักพัฒนาจึง "สังเกต" จึงพบตำแหน่งและสาเหตุของเบรกและปล่อยแพทช์ หากจดหมายข่มขู่จาก DBA ไม่มาถึงอีกในอนาคตอันใกล้นี้ ทุกคนก็จะถอนหายใจด้วยความโล่งอกและกลับไปทำงานปัจจุบันของตน (จนกว่าจะมีจดหมายฉบับใหม่)
แต่กระบวนการตรวจสอบอาจดูสนุกสนานและเป็นมิตรมากขึ้น และที่สำคัญที่สุดคือ เข้าถึงได้และโปร่งใสสำหรับทุกคน อย่างน้อยก็เป็นส่วนพื้นฐาน ซึ่งเป็นส่วนเสริมของระบบตรวจสอบหลัก (ซึ่งมีประโยชน์อย่างแน่นอนและในหลายกรณีไม่สามารถถูกแทนที่ได้) DBMS ใดๆ เป็นอิสระและไม่มีค่าใช้จ่ายใดๆ ในการแบ่งปันข้อมูลเกี่ยวกับสถานะปัจจุบันและประสิทธิภาพการทำงาน ใน Oracle DB ที่ "นองเลือด" เดียวกันนั้น ข้อมูลเกือบทั้งหมดเกี่ยวกับประสิทธิภาพสามารถรับได้จากมุมมองของระบบ ตั้งแต่กระบวนการและเซสชันไปจนถึงสถานะของแคชบัฟเฟอร์ (ตัวอย่างเช่น
ดังนั้นด้วยตัวรวบรวมตัวชี้วัดบางประเภท (Telegraf, Metricbeat, Collectd) ที่สามารถทำการสืบค้น sql แบบกำหนดเอง, ที่เก็บข้อมูลของตัวชี้วัดเหล่านี้ (InfluxDB, Elasticsearch, Timescaledb) และเครื่องมือสร้างภาพ (Grafana, Kibana) คุณจะได้รับค่อนข้างง่าย และระบบตรวจสอบที่ยืดหยุ่นซึ่งจะบูรณาการอย่างใกล้ชิดกับตัวชี้วัดทั่วทั้งระบบ (ได้รับ เช่น จากเซิร์ฟเวอร์แอปพลิเคชัน จากระบบปฏิบัติการ เป็นต้น) ตัวอย่างเช่น สิ่งนี้เสร็จสิ้นใน pgwatch2 ซึ่งใช้ชุดค่าผสม InfluxDB + Grafana และชุดแบบสอบถามไปยังมุมมองของระบบ ซึ่งสามารถเข้าถึงได้เช่นกัน
เบ็ดเสร็จ
และนี่เป็นเพียงรายการโดยประมาณของสิ่งที่สามารถทำได้กับฐานข้อมูลของเราโดยใช้โค้ด SQL ปกติ ฉันแน่ใจว่าคุณสามารถใช้งานได้อีกมากมาย เขียนในความคิดเห็น และเราจะพูดถึงวิธี (และที่สำคัญที่สุดว่าทำไม) เพื่อทำให้ทั้งหมดนี้เป็นแบบอัตโนมัติ และรวมไว้ในไปป์ไลน์ CI/CD ของคุณในครั้งต่อไป
ที่มา: will.com