🦆 Bolagshack

Guide · terminal

DuckDB i terminalen

Sju steg från rå CSV till rena tabeller, med tekniken visad med små exempel. Tabellerna skriver du själv.

Den här sidan visar hur man gör varje moment i uppgiften i terminalen, med små exempel.

Det här är fusklappar. Försök själv först, med dokumentationen och DESCRIBE/SUMMARIZE som stöd. Kika här bara när du kört fast.

1. Titta innan du gissar

DuckDB läser en CSV som om den vore en tabell. Ingen import behövs:

sh
duckdb -c "SELECT * FROM 'data/job_skills.csv' LIMIT 5"

Kolumnerna och typerna DuckDB gissade sig till:

sh
duckdb -c "DESCRIBE SELECT * FROM 'data/job_skills.csv'"

SUMMARIZE ger min, max, antal unika och andel null per kolumn. Det är det snabbaste sättet att hitta det som är stökigt:

sh
duckdb -c "SUMMARIZE SELECT * FROM 'data/people_skills.csv'"

Titta särskilt på kolumner som är VARCHAR men egentligen är något annat: en lista, ett JSON-dokument, ett datum.

2. Spara arbetet i en fil

Öppna en databas på disk så finns tabellerna kvar mellan körningar, och kan öppnas i webgränssnittet (duckdb -ui hack.db) och från Python:

sh
duckdb hack.db
sql
CREATE TABLE jobs   AS SELECT * FROM 'data/job_skills.csv';
CREATE TABLE people AS SELECT * FROM 'data/people_skills.csv';
SHOW TABLES;

CREATE OR REPLACE TABLE är praktiskt när du itererar på en fråga.

Bra att kunna i CLI:t: .tables, .mode line (en kolumn per rad, bra för breda tabeller), .mode duckbox (tillbaka till standard), .quit.

3. En sträng med kommatecken är en lista

skills ser ut så här: "python, sql, azure". Tre funktioner räcker:

sql
SELECT string_split('python, sql, azure', ',');          -- [python,  sql,  azure]
SELECT unnest(string_split('python, sql, azure', ','));  -- en rad per element
SELECT trim(unnest(string_split('python, sql, azure', ',')));  -- utan mellanslag

Kör dem en i taget och titta på vad som händer med resultatet. unnest gör en lista till rader, och de andra kolumnerna i SELECT följer med på varje rad. Det är exakt det som behövs för att gå från en rad per uppdrag till en rad per uppdrag och skill.

Din uppgift: gör en tabell job_skill med kolumnerna job_id, skill, och en person_skill på samma sätt. SELECT DISTINCT tar bort dubbletter. Kontrollera med count(*) att antalet rader är rimligt.

4. JSON i en sträng

skills_detailed i people är en JSON-lista med nivå per skill. DuckDB kan parsa den, och ->> plockar ut ett fält som text:

sql
SELECT s ->> 'skill_name' AS skill, (s ->> 'level')::INT AS level
FROM unnest(from_json('[{"skill_name":"sql","level":4}]', '["json"]')) AS t(s);

Byt ut den hårdkodade strängen mot kolumnen och lägg till FROM people före unnest-delen (samma mönster som i steg 3), så har du nivå per person och skill. Dokumentation: https://duckdb.org/docs/stable/data/json/overview

5. Städa namn

Räkna först. Sorterat efter antal syns dubbletterna direkt:

sql
SELECT skill, count(*) AS n
FROM job_skill
GROUP BY skill
ORDER BY n DESC;

Sök sedan efter varianter av samma sak:

sql
SELECT DISTINCT skill FROM job_skill WHERE skill ILIKE '%azure%';

Det finns inget rätt svar på om "azure devops" är "azure". Men det finns ett bra sätt att uttrycka beslutet: en tabell skill som mappar råa namn till ett kanoniskt, som du sedan joinar mot.

sql
CREATE TABLE skill (raw VARCHAR, name VARCHAR);
INSERT INTO skill VALUES ('microsoft azure', 'azure'), ('azure', 'azure');

Verktyg att fylla den med: lower(), trim(), replace(), CASE WHEN ... ILIKE, och för det luddiga jaro_winkler_similarity(a, b) som ger ett tal 0–1 för hur lika två strängar är. Allt som inte finns i skill-tabellen kan få behålla sitt råa namn (coalesce).

Funktioner: https://duckdb.org/docs/stable/sql/functions/text

6. Joina och räkna

När job_skill, person_skill och skill finns är analysfrågorna vanlig SQL: JOIN, GROUP BY, count(*). Två saker att ha koll på:

7. Spara ut resultatet

sql
COPY person_skill TO 'output/person_skill.parquet';
COPY (SELECT ...) TO 'output/top_skills.csv';

Parquet-filen kan läsas tillbaka med SELECT * FROM 'output/person_skill.parquet', från vilken maskin som helst.