{"id":238,"date":"2026-10-25T17:10:39","date_gmt":"2026-10-25T17:10:39","guid":{"rendered":"https:\/\/devdojo.co.in\/?p=238"},"modified":"2026-10-25T17:10:40","modified_gmt":"2026-10-25T17:10:40","slug":"duckdb-2-tutorial-python-nodejs","status":"publish","type":"post","link":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/25\/duckdb-2-tutorial-python-nodejs\/","title":{"rendered":"DuckDB 2.0 Tutorial: Fast SQL on CSV, Parquet and JSON in Python and Node.js"},"content":{"rendered":"\n<div class=\"dd-post\">\n<style>\n.single article:has(.dd-hero) .entry-media{display:none}\n.dd-post{--navy:#0b1020;--blue:#3178c6;--violet:#7c5cff;--ink:#1f2433;--muted:#5b6275;--line:#e3e6ef;font-size:17px;line-height:1.75;color:var(--ink)}\n.dd-post h2{margin:2.2em 0 .6em;font-size:1.6em;line-height:1.3;scroll-margin-top:90px}\n.dd-post h3{margin:1.6em 0 .5em;font-size:1.22em;scroll-margin-top:90px}\n.dd-post a{color:var(--blue);text-decoration:underline;text-underline-offset:3px}\n.dd-post pre{background:#0f1430;color:#e6e9f5;padding:18px 20px;border-radius:12px;overflow-x:auto;line-height:1.6;border:1px solid #232a52;margin:1.2em 0;font-size:14.5px !important}\n.dd-post pre code{font-size:1em !important;background:none;color:inherit;padding:0;font-family:\"JetBrains Mono\",\"Fira Code\",Consolas,Menlo,monospace;white-space:pre}\n.dd-post :not(pre)>code{background:#eef1fb;color:#2b3a8f;padding:2px 6px;border-radius:5px;font-size:.9em}\n.dd-post .dd-file{display:inline-block;background:#151b3b;color:#c9d1ff;font:600 13px\/1 \"JetBrains Mono\",Consolas,monospace;padding:7px 12px;border-radius:8px 8px 0 0;margin:1em 0 -1.2em;position:relative}\n.dd-post .dd-hero{margin:0 0 1.6em;border-radius:16px;overflow:hidden;box-shadow:0 10px 30px rgba(11,16,32,.25)}\n.dd-post .dd-hero svg{display:block;width:100%;height:auto}\n.dd-post .dd-hero .node{transition:transform .25s,filter .25s;transform-box:fill-box;transform-origin:center;cursor:pointer}\n.dd-post .dd-hero .node:hover{transform:scale(1.08);filter:drop-shadow(0 0 10px #7c5cff)}\n.dd-post .dd-series{display:inline-flex;align-items:center;gap:8px;background:#151b3b;color:#dfe3ff;border-radius:999px;padding:6px 14px;font-size:.88em;font-weight:600;margin:0 0 .8em}\n.dd-post .dd-series span{background:var(--violet);color:#fff;border-radius:999px;padding:2px 10px}\n.dd-post .dd-prereq{font-size:.96em;color:var(--muted);border-left:3px solid var(--line);padding-left:12px;margin:.6em 0 1.2em}\n.dd-post .dd-summary{background:linear-gradient(135deg,#eef3ff,#f4efff);border:1px solid #d6dcf5;border-left:5px solid var(--violet);border-radius:12px;padding:18px 22px;margin:1.4em 0}\n.dd-post .dd-summary strong.lbl{display:block;font-size:1.05em;margin-bottom:6px;color:#3a2fa0}\n.dd-post .dd-summary ul{margin:.3em 0 0 1.1em}\n.dd-post .dd-toc{background:#f8f9fc;border:1px solid var(--line);border-radius:12px;padding:16px 22px;margin:1.4em 0}\n.dd-post .dd-toc ol{margin:.4em 0 0 1.2em;columns:2;column-gap:28px}\n.dd-post .dd-toc li{break-inside:avoid;margin:.15em 0}\n@media(max-width:640px){.dd-post .dd-toc ol{columns:1}}\n.dd-post .dd-tip,.dd-post .dd-note,.dd-post .dd-warn{border-radius:10px;padding:14px 18px;margin:1.3em 0}\n.dd-post .dd-tip{background:#ecfbf3;border-left:4px solid #1f9d61}\n.dd-post .dd-note{background:#eef4ff;border-left:4px solid var(--blue)}\n.dd-post .dd-warn{background:#fff6e8;border-left:4px solid #e08a00}\n.dd-post .dd-cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:14px;margin:1.3em 0}\n.dd-post .dd-card{border:1px solid var(--line);border-radius:12px;padding:14px 16px;background:#fff;box-shadow:0 2px 8px rgba(20,30,70,.05)}\n.dd-post .dd-card b{display:block;color:#3a2fa0;margin-bottom:4px}\n.dd-post table{width:100%;border-collapse:collapse;margin:1.3em 0;font-size:.95em}\n.dd-post th,.dd-post td{border:1px solid var(--line);padding:10px 12px;text-align:left;vertical-align:top}\n.dd-post th{background:#151b3b;color:#fff}\n.dd-post tr:nth-child(even) td{background:#f8f9fc}\n.dd-post .dd-tablewrap{overflow-x:auto}\n.dd-post figure{margin:1.6em 0;text-align:center}\n.dd-post figure svg{width:100%;height:auto;border-radius:12px}\n.dd-post figcaption{font-size:.9em;color:var(--muted);margin-top:8px}\n.dd-post details{border:1px solid var(--line);border-radius:10px;padding:12px 16px;margin:.7em 0;background:#fff}\n.dd-post details summary{cursor:pointer;font-weight:600}\n.dd-post details[open] summary{margin-bottom:8px;color:#3a2fa0}\n.dd-post .dd-roadmap{border:2px solid #d6dcf5;background:#fbfbff}\n.dd-post .dd-roadmap h4{margin:1em 0 .3em;color:#151b3b;font-size:1em;text-transform:uppercase;letter-spacing:.04em}\n.dd-post .dd-roadmap ol{margin:.2em 0 .6em 1.4em;padding:0}\n.dd-post .dd-roadmap li{margin:.2em 0}\n.dd-post .dd-roadmap .here{background:var(--violet);color:#fff;border-radius:999px;padding:1px 9px;font-size:.8em;font-weight:700;margin-left:6px}\n.dd-post .dd-roadmap .soon{color:#8a90a6;font-size:.82em;margin-left:6px}\n.dd-post .dd-tabs{border:1px solid var(--line);border-radius:12px;padding:6px 16px 16px;margin:1.4em 0;background:#fbfbfe}\n.dd-post .dd-tabs>input{position:absolute;opacity:0;pointer-events:none}\n.dd-post .dd-tabs .labels label{display:inline-block;padding:8px 16px;margin:10px 6px 0 0;border-radius:999px;border:1px solid #cfd5ea;cursor:pointer;font-weight:600;font-size:.95em;background:#fff;transition:background .2s}\n.dd-post .dd-tabs .labels label:hover{border-color:var(--violet)}\n.dd-post .dd-tabs .panel{display:none;margin-top:12px}\n.dd-post .dd-tabs #ti1:checked~.labels label[for=ti1],.dd-post .dd-tabs #ti2:checked~.labels label[for=ti2],.dd-post .dd-tabs #ti3:checked~.labels label[for=ti3],.dd-post .dd-tabs #ti4:checked~.labels label[for=ti4]{background:var(--violet);color:#fff;border-color:var(--violet)}\n.dd-post .dd-tabs #ti1:checked~.p1,.dd-post .dd-tabs #ti2:checked~.p2,.dd-post .dd-tabs #ti3:checked~.p3,.dd-post .dd-tabs #ti4:checked~.p4{display:block}\n.dd-post .dd-tabs #ti1:focus-visible~.labels label[for=ti1],.dd-post .dd-tabs #ti2:focus-visible~.labels label[for=ti2],.dd-post .dd-tabs #ti3:focus-visible~.labels label[for=ti3],.dd-post .dd-tabs #ti4:focus-visible~.labels label[for=ti4]{outline:2px solid var(--blue);outline-offset:2px}\n.dd-post .dd-cta{background:linear-gradient(135deg,#0b1020,#151b3b);color:#e8ebff;border-radius:16px;padding:24px 26px;margin:2em 0;text-align:center}\n.dd-post .dd-cta h3{color:#fff;margin-top:0}\n.dd-post .dd-cta a{color:#a99bff}\n.dd-post .dd-vsc{position:relative;margin:1.5em 0;border-radius:12px;overflow:hidden;background:#1e1e1e;border:1px solid #2b2b2b;box-shadow:0 14px 34px rgba(0,0,0,.28);font-family:\"Segoe UI\",system-ui,sans-serif}\n.dd-post .dd-vsc>input{position:absolute;opacity:0;pointer-events:none}\n.dd-post .dd-vsc-bar{display:flex;align-items:center;gap:12px;background:#323233;color:#cccccc;font-size:12.5px;padding:8px 12px}\n.dd-post .dd-dots{display:inline-flex;gap:7px}.dd-post .dd-dots i{width:12px;height:12px;border-radius:50%;background:#ff5f57;display:block}.dd-post .dd-dots i:nth-child(2){background:#febc2e}.dd-post .dd-dots i:nth-child(3){background:#28c840}\n.dd-post .dd-vsc-title{flex:1;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding-right:50px;opacity:.85}\n.dd-post .dd-vsc-tabs{display:flex;align-items:stretch;background:#252526;border-bottom:1px solid #1e1e1e;overflow-x:auto}\n.dd-post .dd-vsc-tabs label{display:flex;align-items:center;gap:7px;padding:9px 16px;color:#969696;font-size:13px;cursor:pointer;background:#2d2d2d;border-right:1px solid #252526;border-top:1px solid transparent;white-space:nowrap;margin:0;transition:color .15s}\n.dd-post .dd-vsc-tabs label:hover{color:#e0e0e0}\n.dd-post .dd-vsc-tabs .ic{font:700 9.5px\/1 Consolas,monospace;padding:3px 4px;border-radius:3px;color:#1e1e1e}\n.dd-post .ic.py{background:#ffd43b}.dd-post .ic.js{background:#f1e05a}.dd-post .ic.sh{background:#89e051}\n.dd-post .dd-vsc-tabs label.run{margin-left:auto;background:transparent;border:0;color:#89d185;font-weight:600;display:none}\n.dd-post .dd-vsc-tabs label.run:hover{color:#b5f0b0;background:#2a2d2e}\n.dd-post .dd-vsc-pane{display:none}\n.dd-post .dd-vsc>.f1:checked~.dd-vsc-tabs .t1,.dd-post .dd-vsc>.f2:checked~.dd-vsc-tabs .t2,.dd-post .dd-vsc>.f3:checked~.dd-vsc-tabs .t3{background:#1e1e1e;color:#ffffff;border-top-color:#3178c6}\n.dd-post .dd-vsc>.f1:checked~.p1,.dd-post .dd-vsc>.f2:checked~.p2,.dd-post .dd-vsc>.f3:checked~.p3{display:block}\n.dd-post .dd-vsc>.f1:checked~.dd-vsc-tabs .run1,.dd-post .dd-vsc>.f2:checked~.dd-vsc-tabs .run2,.dd-post .dd-vsc>.f3:checked~.dd-vsc-tabs .run3{display:flex}\n.dd-post .dd-vsc>.f1:focus-visible~.dd-vsc-tabs .t1,.dd-post .dd-vsc>.f2:focus-visible~.dd-vsc-tabs .t2,.dd-post .dd-vsc>.f3:focus-visible~.dd-vsc-tabs .t3,.dd-post .dd-vsc>input[type=checkbox]:focus-visible~.dd-vsc-tabs .run{outline:2px solid #3178c6;outline-offset:-2px}\n.dd-post .dd-vsc-crumbs{background:#1e1e1e;color:#a9a9a9;font-size:12px;padding:5px 16px 2px}\n.dd-post .dd-vsc pre.dd-vsc-code{background:#1e1e1e;color:#d4d4d4;border:0;border-radius:0;margin:0;padding:8px 0 14px;counter-reset:ln;line-height:1.62;overflow-x:auto}\n.dd-post .dd-vsc pre code{font-family:Consolas,\"JetBrains Mono\",\"Fira Code\",Menlo,monospace;color:inherit;background:none;white-space:pre}\n.dd-post .dd-vsc .l{display:inline-block;min-width:100%;padding-right:18px;box-sizing:border-box}\n.dd-post .dd-vsc .l::before{counter-increment:ln;content:counter(ln);display:inline-block;width:3.2em;padding-right:1.4em;text-align:right;color:#6e7681;user-select:none;-webkit-user-select:none}\n.dd-post .dd-vsc .l:hover{background:#2a2d2e}.dd-post .dd-vsc .l:hover::before{color:#c6c6c6}\n.dd-post .dd-vsc .k,.dd-post .dd-vsc .kn{color:#c586c0}.dd-post .dd-vsc .kc{color:#569cd6}.dd-post .dd-vsc .s{color:#ce9178}.dd-post .dd-vsc .c{color:#6a9955}.dd-post .dd-vsc .m{color:#b5cea8}.dd-post .dd-vsc .nf{color:#dcdcaa}.dd-post .dd-vsc .nc{color:#4ec9b0}.dd-post .dd-vsc .nb{color:#4ec9b0}.dd-post .dd-vsc .nd{color:#dcdcaa}.dd-post .dd-vsc .n{color:#9cdcfe}.dd-post .dd-vsc .o{color:#d4d4d4}.dd-post .dd-vsc .gp{color:#89d185;user-select:none;-webkit-user-select:none}\n.dd-post .dd-vsc-term{display:none;border-top:1px solid #3c3c3c;background:#181818}\n.dd-post .dd-vsc>.r1:checked~.p1 .dd-vsc-term,.dd-post .dd-vsc>.r2:checked~.p2 .dd-vsc-term,.dd-post .dd-vsc>.r3:checked~.p3 .dd-vsc-term{display:block;animation:ddterm .35s ease-out}\n@keyframes ddterm{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}\n.dd-post .dd-term-head{display:flex;gap:18px;align-items:center;font-size:11px;letter-spacing:.06em;color:#8b8b8b;padding:7px 16px 0}\n.dd-post .dd-term-head span:first-child{color:#e7e7e7;border-bottom:1px solid #e7e7e7;padding-bottom:4px}\n.dd-post .dd-term-head .dd-term-note{margin-left:auto;letter-spacing:0;text-transform:none;color:#d7ba7d;border:0;font-size:11.5px}\n.dd-post .dd-vsc-term pre{background:#181818;color:#cccccc;border:0;margin:0;padding:10px 16px 14px;border-radius:0}\n.dd-post .dd-vsc-status{display:flex;flex-wrap:wrap;gap:4px 14px;background:#007acc;color:#fff;font-size:11.5px;padding:4px 12px}\n.dd-post .dd-vsc-status .sp{flex:1}\n.dd-post .dd-vsc-termonly pre.dd-vsc-code{background:#181818;padding:12px 16px}\n@media(max-width:640px){.dd-post .dd-vsc-title{padding-right:0;text-align:left}.dd-post .dd-vsc-status span:nth-child(n+4){display:none}.dd-post .dd-vsc .l::before{width:2.2em;padding-right:1em}}\n.dd-post .dd-vsc-hint{font-size:.93em;color:var(--muted);margin-top:-.6em}\n.dd-post .dd-next{border:1px dashed var(--violet);border-radius:12px;padding:16px 20px;margin:1.6em 0;background:#f7f4ff}\n.dd-post .ic.yml{background:#cb3837;color:#fff}\n.dd-post .ic.jsx{background:#61dafb}.dd-post .ic.html{background:#e44d26;color:#fff}.dd-post .ic.css{background:#563d7c;color:#fff}\n.dd-post .dd-steps{counter-reset:st;list-style:none;margin:1.2em 0;padding:0}\n.dd-post .dd-steps>li{position:relative;padding:0 0 0 46px;margin:0 0 14px}\n.dd-post .dd-steps>li::before{counter-increment:st;content:counter(st);position:absolute;left:0;top:2px;width:32px;height:32px;border-radius:50%;background:linear-gradient(135deg,#3178c6,#7c5cff);color:#fff;font-weight:700;text-align:center;line-height:32px}\n<\/style>\n<style>\n.dd-post .ic.sql{background:#e38c00;color:#fff}.dd-post .ic.csv{background:#89e051}\n.dd-post .dd-vs{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin:1.2em 0}\n@media(max-width:640px){.dd-post .dd-vs{grid-template-columns:1fr}}\n.dd-post .dd-badge{display:inline-block;background:#151b3b;color:#fff;border-radius:999px;padding:2px 10px;font-size:.8em;font-weight:700;margin-right:6px}\n<\/style>\n\n<div class=\"dd-hero\">\n<svg viewBox=\"0 0 1200 520\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" role=\"img\" aria-label=\"DuckDB 2.0 tutorial banner: CSV, Parquet and JSON files flow into an in-process SQL engine and come out as a bar chart of results\">\n<defs>\n<linearGradient id=\"ddbg\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"1\"><stop offset=\"0\" stop-color=\"#0b1020\"\/><stop offset=\"1\" stop-color=\"#151b3b\"\/><\/linearGradient>\n<linearGradient id=\"ddac\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"0\"><stop offset=\"0\" stop-color=\"#3178c6\"\/><stop offset=\"1\" stop-color=\"#7c5cff\"\/><\/linearGradient>\n<linearGradient id=\"ddyl\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"1\"><stop offset=\"0\" stop-color=\"#ffe066\"\/><stop offset=\"1\" stop-color=\"#f5b700\"\/><\/linearGradient>\n<radialGradient id=\"ddglow\" cx=\".5\" cy=\".5\" r=\".5\"><stop offset=\"0\" stop-color=\"#7c5cff\" stop-opacity=\".45\"\/><stop offset=\"1\" stop-color=\"#7c5cff\" stop-opacity=\"0\"\/><\/radialGradient>\n<\/defs>\n<rect x=\"0\" y=\"-60\" width=\"1200\" height=\"640\" fill=\"url(#ddbg)\"\/>\n<circle cx=\"900\" cy=\"300\" r=\"280\" fill=\"url(#ddglow)\"\/>\n<g opacity=\".12\" stroke=\"#9fb2ff\"><path d=\"M0 100H1200M0 200H1200M0 300H1200M0 400H1200M200 0V520M400 0V520M600 0V520M800 0V520M1000 0V520\"\/><\/g>\n<rect x=\"60\" y=\"46\" width=\"150\" height=\"38\" rx=\"19\" fill=\"url(#ddac)\"\/>\n<text x=\"135\" y=\"71\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"18\" font-weight=\"700\" fill=\"#fff\">v2.0 \u00b7 LTS<\/text>\n<text x=\"226\" y=\"71\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"18\" fill=\"#aab4e8\">Databases \u00b7 DevDojo<\/text>\n<text x=\"60\" y=\"148\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"54\" font-weight=\"800\" fill=\"#ffffff\">DuckDB 2.0 Tutorial<\/text>\n<text x=\"60\" y=\"208\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"40\" font-weight=\"800\" fill=\"#c9d1ff\">Fast SQL on CSV, Parquet and JSON<\/text>\n<text x=\"60\" y=\"252\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"24\" fill=\"#aab4e8\">Python and Node.js \u00b7 No server \u00b7 Runs on your laptop<\/text>\n<g class=\"node\"><rect x=\"60\" y=\"300\" width=\"130\" height=\"46\" rx=\"10\" fill=\"#0b1020\" stroke=\"#89e051\" stroke-width=\"2\"\/><text x=\"125\" y=\"330\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"18\" font-weight=\"700\" fill=\"#d8ffd0\">sales.csv<\/text><\/g>\n<g class=\"node\"><rect x=\"60\" y=\"360\" width=\"130\" height=\"46\" rx=\"10\" fill=\"#0b1020\" stroke=\"#3178c6\" stroke-width=\"2\"\/><text x=\"125\" y=\"390\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"18\" font-weight=\"700\" fill=\"#c9d1ff\">.parquet<\/text><\/g>\n<g class=\"node\"><rect x=\"60\" y=\"420\" width=\"130\" height=\"46\" rx=\"10\" fill=\"#0b1020\" stroke=\"#7c5cff\" stroke-width=\"2\"\/><text x=\"125\" y=\"450\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"18\" font-weight=\"700\" fill=\"#e0d8ff\">events.json<\/text><\/g>\n<path d=\"M190 323H260V383H330M190 383H330M190 443H260V383H330\" stroke=\"#7c5cff\" stroke-width=\"2.5\" fill=\"none\" opacity=\".8\"\/>\n<g class=\"node\"><rect x=\"330\" y=\"335\" width=\"230\" height=\"96\" rx=\"20\" fill=\"url(#ddac)\"\/><text x=\"445\" y=\"375\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"24\" font-weight=\"800\" fill=\"#fff\">SQL engine<\/text><text x=\"445\" y=\"405\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"16\" fill=\"#e8ebff\">inside your app<\/text><\/g>\n<path d=\"M560 383H640\" stroke=\"#ffd43b\" stroke-width=\"3\" fill=\"none\"\/>\n<path id=\"ddf1\" d=\"M190 323H260V383H560\" fill=\"none\" stroke=\"none\"\/>\n<path id=\"ddf2\" d=\"M190 443H260V383H560\" fill=\"none\" stroke=\"none\"\/>\n<path id=\"ddf3\" d=\"M190 383H640\" fill=\"none\" stroke=\"none\"\/>\n<circle r=\"6\" fill=\"#89e051\"><animateMotion dur=\"3s\" repeatCount=\"indefinite\"><mpath href=\"#ddf1\"\/><\/animateMotion><\/circle>\n<circle r=\"6\" fill=\"#61dafb\"><animateMotion dur=\"3.4s\" begin=\"1s\" repeatCount=\"indefinite\"><mpath href=\"#ddf3\"\/><\/animateMotion><\/circle>\n<circle r=\"6\" fill=\"#c9b8ff\"><animateMotion dur=\"3.8s\" begin=\"2s\" repeatCount=\"indefinite\"><mpath href=\"#ddf2\"\/><\/animateMotion><\/circle>\n<g class=\"node\" transform=\"translate(660 290)\">\n<rect x=\"0\" y=\"0\" width=\"300\" height=\"180\" rx=\"16\" fill=\"#0b1020\" stroke=\"#2c3566\" stroke-width=\"2\"\/>\n<rect x=\"30\" y=\"110\" width=\"40\" height=\"50\" rx=\"4\" fill=\"#3178c6\"\/>\n<rect x=\"85\" y=\"70\" width=\"40\" height=\"90\" rx=\"4\" fill=\"#5a6cf0\"\/>\n<rect x=\"140\" y=\"40\" width=\"40\" height=\"120\" rx=\"4\" fill=\"#7c5cff\"\/>\n<rect x=\"195\" y=\"85\" width=\"40\" height=\"75\" rx=\"4\" fill=\"#5a6cf0\"\/>\n<rect x=\"250\" y=\"20\" width=\"30\" height=\"140\" rx=\"4\" fill=\"url(#ddyl)\"><animate attributeName=\"opacity\" values=\"1;.55;1\" dur=\"2.4s\" repeatCount=\"indefinite\"\/><\/rect>\n<text x=\"20\" y=\"30\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"15\" fill=\"#aab4e8\">Results in milliseconds<\/text>\n<\/g>\n<text x=\"1000\" y=\"360\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"17\" fill=\"#aab4e8\">GROUP BY<\/text>\n<text x=\"1000\" y=\"388\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"17\" fill=\"#aab4e8\">JOIN<\/text>\n<text x=\"1000\" y=\"416\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"17\" fill=\"#aab4e8\">window<\/text>\n<text x=\"60\" y=\"505\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"16\" fill=\"#7f89b5\">devdojo.co.in \u00b7 Beginner friendly \u00b7 Tested with DuckDB 2.0.0, Python 3.13 and Node.js 22<\/text>\n<\/svg>\n<\/div>\n\n<p><strong>DuckDB 2.0<\/strong> was released on 21 October and it is one of the most talked-about tools in data engineering right now. In this <strong>DuckDB tutorial<\/strong> you will learn what DuckDB is, why developers call it &#8220;SQLite for analytics&#8221;, and how to query CSV, Parquet and JSON files with plain SQL from <strong>Python<\/strong> and <strong>Node.js<\/strong> \u2013 no database server, no Docker, no import step. Every example below was actually run on DuckDB 2.0.0, and the terminal output you see is real.<\/p>\n\n<div class=\"dd-summary\"><strong class=\"lbl\">Quick Summary<\/strong>\n<ul>\n<li><strong>DuckDB<\/strong> is a free, open-source analytics database that runs <em>inside<\/em> your program (like SQLite), but is built for fast reports, aggregations and big files.<\/li>\n<li>You can run SQL directly on files: <code>SELECT * FROM 'sales.csv'<\/code>.<\/li>\n<li><strong>DuckDB 2.0 (&#8220;Cinnamon Teal&#8221;)<\/strong> is a long-term support release (supported until at least October 2027) with a new SQL parser, a <code>VARIANT<\/code> type, triggers, an agent mode for the CLI and faster file reading.<\/li>\n<li>Upgrading from 1.x? Python now needs <strong>3.11+<\/strong>, relations lost <code>fetchone()<\/code>, the old <code>x -&gt; x + 1<\/code> lambda syntax is off by default, and 1.x cannot open new 2.0 database files.<\/li>\n<\/ul><\/div>\n\n<nav class=\"dd-toc\" aria-label=\"Table of contents\"><strong>Table of contents<\/strong><ol><li><a href=\"#what-is-duckdb\">What is DuckDB?<\/a><\/li><li><a href=\"#whats-new\">What&#8217;s new in DuckDB 2.0<\/a><\/li><li><a href=\"#install\">Install DuckDB<\/a><\/li><li><a href=\"#first-query\">Your first query on a CSV file<\/a><\/li><li><a href=\"#save\">Save to a database file and Parquet<\/a><\/li><li><a href=\"#pandas\">DuckDB with pandas<\/a><\/li><li><a href=\"#speed\">How fast is it?<\/a><\/li><li><a href=\"#v2-features\">Try the new 2.0 features<\/a><\/li><li><a href=\"#triggers\">Triggers: automatic audit log<\/a><\/li><li><a href=\"#python-changes\">Python API changes in 2.0<\/a><\/li><li><a href=\"#upgrade\">Upgrade from DuckDB 1.x<\/a><\/li><li><a href=\"#compare\">DuckDB vs SQLite vs PostgreSQL<\/a><\/li><li><a href=\"#errors\">Common errors and fixes<\/a><\/li><li><a href=\"#best-practices\">Best practices<\/a><\/li><li><a href=\"#practice\">Practice task<\/a><\/li><li><a href=\"#faq\">FAQ<\/a><\/li><\/ol><\/nav>\n<\/div>\n\n\n\n<div class=\"dd-post\">\n<h2 id=\"what-is-duckdb\">What is DuckDB? (DuckDB tutorial basics)<\/h2>\n<p>DuckDB is an <strong>in-process analytical database<\/strong>. Let&#8217;s break that sentence into simple words:<\/p>\n<ul>\n<li><strong>In-process<\/strong> \u2013 there is no separate server to install or start. You install a library with <code>pip<\/code> or <code>npm<\/code>, and the database runs inside your Python or Node.js program.<\/li>\n<li><strong>Analytical<\/strong> \u2013 it is made for questions like &#8220;total revenue per city per month&#8221; over millions of rows, not for saving one user&#8217;s profile thousands of times per second.<\/li>\n<li><strong>Database<\/strong> \u2013 it speaks full SQL: <code>JOIN<\/code>, <code>GROUP BY<\/code>, window functions, CTEs and more. You can keep data in memory or in a single <code>.duckdb<\/code> file.<\/li>\n<\/ul>\n<figure>\n<svg viewBox=\"0 0 900 340\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" role=\"img\" aria-label=\"Diagram comparing a client-server database, where the app talks to a separate database server over the network, with DuckDB, which runs inside the app process and reads files directly\">\n<rect width=\"900\" height=\"340\" fill=\"#0b1020\"\/>\n<text x=\"225\" y=\"36\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"20\" font-weight=\"700\" fill=\"#fff\">Client-server (PostgreSQL, MySQL)<\/text>\n<text x=\"675\" y=\"36\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"20\" font-weight=\"700\" fill=\"#fff\">In-process (DuckDB, SQLite)<\/text>\n<line x1=\"450\" y1=\"20\" x2=\"450\" y2=\"320\" stroke=\"#2c3566\" stroke-width=\"2\" stroke-dasharray=\"6 6\"\/>\n<rect x=\"40\" y=\"80\" width=\"150\" height=\"80\" rx=\"12\" fill=\"#151b3b\" stroke=\"#3178c6\" stroke-width=\"2\"\/>\n<text x=\"115\" y=\"126\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"18\" font-weight=\"700\" fill=\"#c9d1ff\">Your app<\/text>\n<rect x=\"260\" y=\"80\" width=\"160\" height=\"80\" rx=\"12\" fill=\"#151b3b\" stroke=\"#7c5cff\" stroke-width=\"2\"\/>\n<text x=\"340\" y=\"116\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"18\" font-weight=\"700\" fill=\"#e0d8ff\">DB server<\/text>\n<text x=\"340\" y=\"140\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#aab4e8\">install, run, manage<\/text>\n<path d=\"M190 112H260M260 128H190\" stroke=\"#aab4e8\" stroke-width=\"2\"\/>\n<text x=\"225\" y=\"190\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#aab4e8\">network + login<\/text>\n<rect x=\"260\" y=\"220\" width=\"160\" height=\"60\" rx=\"10\" fill=\"#151b3b\" stroke=\"#2c3566\" stroke-width=\"2\"\/>\n<text x=\"340\" y=\"256\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"15\" fill=\"#c9d1ff\">data must be imported<\/text>\n<path d=\"M340 160V220\" stroke=\"#aab4e8\" stroke-width=\"2\"\/>\n<rect x=\"500\" y=\"70\" width=\"350\" height=\"120\" rx=\"14\" fill=\"#151b3b\" stroke=\"#3178c6\" stroke-width=\"2\"\/>\n<text x=\"525\" y=\"100\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"18\" font-weight=\"700\" fill=\"#c9d1ff\">Your app (Python \/ Node.js)<\/text>\n<rect x=\"525\" y=\"115\" width=\"300\" height=\"56\" rx=\"10\" fill=\"url(#ddac2)\"\/>\n<defs><linearGradient id=\"ddac2\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"0\"><stop offset=\"0\" stop-color=\"#3178c6\"\/><stop offset=\"1\" stop-color=\"#7c5cff\"\/><\/linearGradient><\/defs>\n<text x=\"675\" y=\"150\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"18\" font-weight=\"800\" fill=\"#fff\">DuckDB library (pip \/ npm)<\/text>\n<g font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"15\" font-weight=\"700\">\n<rect x=\"510\" y=\"235\" width=\"100\" height=\"44\" rx=\"8\" fill=\"#0b1020\" stroke=\"#89e051\" stroke-width=\"2\"\/><text x=\"560\" y=\"263\" text-anchor=\"middle\" fill=\"#d8ffd0\">CSV<\/text>\n<rect x=\"625\" y=\"235\" width=\"100\" height=\"44\" rx=\"8\" fill=\"#0b1020\" stroke=\"#3178c6\" stroke-width=\"2\"\/><text x=\"675\" y=\"263\" text-anchor=\"middle\" fill=\"#c9d1ff\">Parquet<\/text>\n<rect x=\"740\" y=\"235\" width=\"100\" height=\"44\" rx=\"8\" fill=\"#0b1020\" stroke=\"#7c5cff\" stroke-width=\"2\"\/><text x=\"790\" y=\"263\" text-anchor=\"middle\" fill=\"#e0d8ff\">JSON<\/text>\n<\/g>\n<path d=\"M560 235V171M675 235V171M790 235V171\" stroke=\"#ffd43b\" stroke-width=\"2\" stroke-dasharray=\"4 4\"\/>\n<text x=\"675\" y=\"310\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#aab4e8\">reads files directly \u2013 no server, no import step<\/text>\n<\/svg>\n<figcaption>Figure 1: A normal database is a separate server. DuckDB is a library that runs inside your program, like SQLite \u2013 but built for analytics.<\/figcaption>\n<\/figure>\n<p>The second big idea is <strong>column storage<\/strong>. A normal app database (PostgreSQL, MySQL, SQLite) stores data row by row. DuckDB stores and processes it column by column, in batches. For reports that read only a few columns from a big table, this is much faster.<\/p>\n<figure>\n<svg viewBox=\"0 0 900 330\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" role=\"img\" aria-label=\"Diagram showing row storage reading whole rows versus column storage reading only the city and price columns for a revenue-by-city query\">\n<rect width=\"900\" height=\"330\" fill=\"#0b1020\"\/>\n<text x=\"220\" y=\"40\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"19\" font-weight=\"700\" fill=\"#fff\">Row storage (good for apps)<\/text>\n<text x=\"690\" y=\"40\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"19\" font-weight=\"700\" fill=\"#fff\">Column storage (DuckDB, Parquet)<\/text>\n<rect x=\"40\" y=\"70\" width=\"90\" height=\"34\" fill=\"#151b3b\" stroke=\"#2c3566\" opacity=\"1\"\/><text x=\"85.0\" y=\"92\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#fff\" opacity=\"1\">id<\/text><rect x=\"130\" y=\"70\" width=\"90\" height=\"34\" fill=\"#151b3b\" stroke=\"#2c3566\" opacity=\"1\"\/><text x=\"175.0\" y=\"92\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#fff\" opacity=\"1\">city<\/text><rect x=\"220\" y=\"70\" width=\"90\" height=\"34\" fill=\"#151b3b\" stroke=\"#2c3566\" opacity=\"1\"\/><text x=\"265.0\" y=\"92\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#fff\" opacity=\"1\">product<\/text><rect x=\"310\" y=\"70\" width=\"90\" height=\"34\" fill=\"#151b3b\" stroke=\"#2c3566\" opacity=\"1\"\/><text x=\"355.0\" y=\"92\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#fff\" opacity=\"1\">price<\/text><rect x=\"40\" y=\"104\" width=\"90\" height=\"34\" fill=\"#121838\" stroke=\"#2c3566\" opacity=\"1\"\/><text x=\"85.0\" y=\"126\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#c9d1ff\" opacity=\"1\">1<\/text><rect x=\"130\" y=\"104\" width=\"90\" height=\"34\" fill=\"#121838\" stroke=\"#2c3566\" opacity=\"1\"\/><text x=\"175.0\" y=\"126\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#c9d1ff\" opacity=\"1\">Delhi<\/text><rect x=\"220\" y=\"104\" width=\"90\" height=\"34\" fill=\"#121838\" stroke=\"#2c3566\" opacity=\"1\"\/><text x=\"265.0\" y=\"126\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#c9d1ff\" opacity=\"1\">Phone<\/text><rect x=\"310\" y=\"104\" width=\"90\" height=\"34\" fill=\"#121838\" stroke=\"#2c3566\" opacity=\"1\"\/><text x=\"355.0\" y=\"126\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#c9d1ff\" opacity=\"1\">18000<\/text><rect x=\"40\" y=\"138\" width=\"90\" height=\"34\" fill=\"#0f1430\" stroke=\"#2c3566\" opacity=\"1\"\/><text x=\"85.0\" y=\"160\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#c9d1ff\" opacity=\"1\">2<\/text><rect x=\"130\" y=\"138\" width=\"90\" height=\"34\" fill=\"#0f1430\" stroke=\"#2c3566\" opacity=\"1\"\/><text x=\"175.0\" y=\"160\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#c9d1ff\" opacity=\"1\">Pune<\/text><rect x=\"220\" y=\"138\" width=\"90\" height=\"34\" fill=\"#0f1430\" stroke=\"#2c3566\" opacity=\"1\"\/><text x=\"265.0\" y=\"160\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#c9d1ff\" opacity=\"1\">Laptop<\/text><rect x=\"310\" y=\"138\" width=\"90\" height=\"34\" fill=\"#0f1430\" stroke=\"#2c3566\" opacity=\"1\"\/><text x=\"355.0\" y=\"160\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#c9d1ff\" opacity=\"1\">55000<\/text><rect x=\"40\" y=\"172\" width=\"90\" height=\"34\" fill=\"#121838\" stroke=\"#2c3566\" opacity=\"1\"\/><text x=\"85.0\" y=\"194\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#c9d1ff\" opacity=\"1\">3<\/text><rect x=\"130\" y=\"172\" width=\"90\" height=\"34\" fill=\"#121838\" stroke=\"#2c3566\" opacity=\"1\"\/><text x=\"175.0\" y=\"194\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#c9d1ff\" opacity=\"1\">Delhi<\/text><rect x=\"220\" y=\"172\" width=\"90\" height=\"34\" fill=\"#121838\" stroke=\"#2c3566\" opacity=\"1\"\/><text x=\"265.0\" y=\"194\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#c9d1ff\" opacity=\"1\">Monitor<\/text><rect x=\"310\" y=\"172\" width=\"90\" height=\"34\" fill=\"#121838\" stroke=\"#2c3566\" opacity=\"1\"\/><text x=\"355.0\" y=\"194\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#c9d1ff\" opacity=\"1\">12000<\/text><rect x=\"40\" y=\"206\" width=\"90\" height=\"34\" fill=\"#0f1430\" stroke=\"#2c3566\" opacity=\"1\"\/><text x=\"85.0\" y=\"228\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#c9d1ff\" opacity=\"1\">4<\/text><rect x=\"130\" y=\"206\" width=\"90\" height=\"34\" fill=\"#0f1430\" stroke=\"#2c3566\" opacity=\"1\"\/><text x=\"175.0\" y=\"228\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#c9d1ff\" opacity=\"1\">Mumbai<\/text><rect x=\"220\" y=\"206\" width=\"90\" height=\"34\" fill=\"#0f1430\" stroke=\"#2c3566\" opacity=\"1\"\/><text x=\"265.0\" y=\"228\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#c9d1ff\" opacity=\"1\">Phone<\/text><rect x=\"310\" y=\"206\" width=\"90\" height=\"34\" fill=\"#0f1430\" stroke=\"#2c3566\" opacity=\"1\"\/><text x=\"355.0\" y=\"228\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#c9d1ff\" opacity=\"1\">18000<\/text><rect x=\"36\" y=\"134\" width=\"368\" height=\"40\" rx=\"6\" fill=\"none\" stroke=\"#ff8a65\" stroke-width=\"3\"\/><rect x=\"500\" y=\"70\" width=\"85\" height=\"34\" fill=\"#151b3b\" stroke=\"#2c3566\" opacity=\".35\"\/><text x=\"542.5\" y=\"92\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#fff\" opacity=\".35\">id<\/text><rect x=\"500\" y=\"104\" width=\"85\" height=\"34\" fill=\"#0f1430\" stroke=\"#2c3566\" opacity=\".35\"\/><text x=\"542.5\" y=\"126\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#c9d1ff\" opacity=\".35\">1<\/text><rect x=\"500\" y=\"138\" width=\"85\" height=\"34\" fill=\"#0f1430\" stroke=\"#2c3566\" opacity=\".35\"\/><text x=\"542.5\" y=\"160\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#c9d1ff\" opacity=\".35\">2<\/text><rect x=\"500\" y=\"172\" width=\"85\" height=\"34\" fill=\"#0f1430\" stroke=\"#2c3566\" opacity=\".35\"\/><text x=\"542.5\" y=\"194\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#c9d1ff\" opacity=\".35\">3<\/text><rect x=\"500\" y=\"206\" width=\"85\" height=\"34\" fill=\"#0f1430\" stroke=\"#2c3566\" opacity=\".35\"\/><text x=\"542.5\" y=\"228\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#c9d1ff\" opacity=\".35\">4<\/text><rect x=\"595\" y=\"70\" width=\"85\" height=\"34\" fill=\"#151b3b\" stroke=\"#2c3566\" opacity=\"1\"\/><text x=\"637.5\" y=\"92\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#fff\" opacity=\"1\">city<\/text><rect x=\"595\" y=\"104\" width=\"85\" height=\"34\" fill=\"#1b2350\" stroke=\"#2c3566\" opacity=\"1\"\/><text x=\"637.5\" y=\"126\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#ffffff\" opacity=\"1\">Delhi<\/text><rect x=\"595\" y=\"138\" width=\"85\" height=\"34\" fill=\"#1b2350\" stroke=\"#2c3566\" opacity=\"1\"\/><text x=\"637.5\" y=\"160\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#ffffff\" opacity=\"1\">Pune<\/text><rect x=\"595\" y=\"172\" width=\"85\" height=\"34\" fill=\"#1b2350\" stroke=\"#2c3566\" opacity=\"1\"\/><text x=\"637.5\" y=\"194\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#ffffff\" opacity=\"1\">Delhi<\/text><rect x=\"595\" y=\"206\" width=\"85\" height=\"34\" fill=\"#1b2350\" stroke=\"#2c3566\" opacity=\"1\"\/><text x=\"637.5\" y=\"228\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#ffffff\" opacity=\"1\">Mumbai<\/text><rect x=\"591\" y=\"66\" width=\"93\" height=\"178\" rx=\"8\" fill=\"none\" stroke=\"#ffd43b\" stroke-width=\"3\"\/><rect x=\"690\" y=\"70\" width=\"85\" height=\"34\" fill=\"#151b3b\" stroke=\"#2c3566\" opacity=\".35\"\/><text x=\"732.5\" y=\"92\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#fff\" opacity=\".35\">product<\/text><rect x=\"690\" y=\"104\" width=\"85\" height=\"34\" fill=\"#0f1430\" stroke=\"#2c3566\" opacity=\".35\"\/><text x=\"732.5\" y=\"126\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#c9d1ff\" opacity=\".35\">Phone<\/text><rect x=\"690\" y=\"138\" width=\"85\" height=\"34\" fill=\"#0f1430\" stroke=\"#2c3566\" opacity=\".35\"\/><text x=\"732.5\" y=\"160\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#c9d1ff\" opacity=\".35\">Laptop<\/text><rect x=\"690\" y=\"172\" width=\"85\" height=\"34\" fill=\"#0f1430\" stroke=\"#2c3566\" opacity=\".35\"\/><text x=\"732.5\" y=\"194\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#c9d1ff\" opacity=\".35\">Monitor<\/text><rect x=\"690\" y=\"206\" width=\"85\" height=\"34\" fill=\"#0f1430\" stroke=\"#2c3566\" opacity=\".35\"\/><text x=\"732.5\" y=\"228\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#c9d1ff\" opacity=\".35\">Phone<\/text><rect x=\"785\" y=\"70\" width=\"85\" height=\"34\" fill=\"#151b3b\" stroke=\"#2c3566\" opacity=\"1\"\/><text x=\"827.5\" y=\"92\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#fff\" opacity=\"1\">price<\/text><rect x=\"785\" y=\"104\" width=\"85\" height=\"34\" fill=\"#1b2350\" stroke=\"#2c3566\" opacity=\"1\"\/><text x=\"827.5\" y=\"126\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#ffffff\" opacity=\"1\">18000<\/text><rect x=\"785\" y=\"138\" width=\"85\" height=\"34\" fill=\"#1b2350\" stroke=\"#2c3566\" opacity=\"1\"\/><text x=\"827.5\" y=\"160\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#ffffff\" opacity=\"1\">55000<\/text><rect x=\"785\" y=\"172\" width=\"85\" height=\"34\" fill=\"#1b2350\" stroke=\"#2c3566\" opacity=\"1\"\/><text x=\"827.5\" y=\"194\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#ffffff\" opacity=\"1\">12000<\/text><rect x=\"785\" y=\"206\" width=\"85\" height=\"34\" fill=\"#1b2350\" stroke=\"#2c3566\" opacity=\"1\"\/><text x=\"827.5\" y=\"228\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#ffffff\" opacity=\"1\">18000<\/text><rect x=\"781\" y=\"66\" width=\"93\" height=\"178\" rx=\"8\" fill=\"none\" stroke=\"#ffd43b\" stroke-width=\"3\"\/>\n<text x=\"220\" y=\"282\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"15\" fill=\"#ffb59e\">Reads whole rows, even unused columns<\/text>\n<text x=\"690\" y=\"282\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"15\" fill=\"#ffe28a\">Reads only city + price for SUM(price) BY city<\/text>\n<text x=\"450\" y=\"314\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#aab4e8\">Less data to read + compressed columns + vectorized processing = fast analytics<\/text>\n<\/svg>\n<figcaption>Figure 2: For a &#8220;revenue by city&#8221; report, DuckDB only touches the two columns it needs. That is a big reason it is so fast on large files.<\/figcaption>\n<\/figure>\n<div class=\"dd-cards\">\n<div class=\"dd-card\"><b>Zero setup<\/b>One <code>pip install<\/code>. No server, no users, no ports.<\/div>\n<div class=\"dd-card\"><b>Query files directly<\/b>CSV, Parquet, JSON \u2013 even files on S3 or a URL (with the httpfs extension).<\/div>\n<div class=\"dd-card\"><b>Very fast<\/b>Columnar, vectorized engine that uses all your CPU cores.<\/div>\n<div class=\"dd-card\"><b>Plays well with others<\/b>Reads and returns pandas, Polars and Arrow data.<\/div>\n<\/div>\n<p>Typical uses: data analysis in Jupyter, cleaning and converting files (CSV to Parquet), small data pipelines, dashboards and APIs that show reports, testing SQL locally, and even giving AI agents a fast SQL tool.<\/p>\n\n<h2 id=\"whats-new\">What&#8217;s new in DuckDB 2.0<\/h2>\n<p>DuckDB 2.0 (code name <strong>&#8220;Cinnamon Teal&#8221;<\/strong>) is built from close to 20,000 commits since version 1.5. The team calls it &#8220;above all a feature release&#8221;. The highlights, from the <a href=\"https:\/\/duckdb.org\/2026\/10\/21\/announcing-duckdb-200.html\" rel=\"noopener\" target=\"_blank\">official DuckDB 2.0 announcement<\/a>:<\/p>\n<div class=\"dd-tablewrap\"><table>\n<thead><tr><th>Feature<\/th><th>What it means for you<\/th><\/tr><\/thead>\n<tbody>\n<tr><td><strong>Long-term support<\/strong><\/td><td>2.0 is supported until at least October 2027 \u2013 a safe choice for production.<\/td><\/tr>\n<tr><td><strong>New SQL parser (PEG)<\/strong><\/td><td>Replaces the old PostgreSQL-based parser. Error messages now point to the exact place in your query.<\/td><\/tr>\n<tr><td><strong><code>VARIANT<\/code> type<\/strong><\/td><td>Store mixed, schema-less data (like JSON) in a fast binary form.<\/td><\/tr>\n<tr><td><strong>Triggers<\/strong><\/td><td><code>CREATE TRIGGER<\/code> with <code>BEFORE<\/code>\/<code>AFTER<\/code>, row and statement level.<\/td><\/tr>\n<tr><td><strong>Asynchronous I\/O<\/strong><\/td><td>Faster reading of Parquet, CSV and DuckDB files \u2013 especially from S3.<\/td><\/tr>\n<tr><td><strong>Agent mode in the CLI<\/strong><\/td><td>Detects AI coding agents and prints compact, agent-friendly output.<\/td><\/tr>\n<tr><td><strong>Server mode (Quack) and <code>CONNECT<\/code><\/strong><\/td><td>Share one DuckDB over the network. Still new \u2013 treat it as early.<\/td><\/tr>\n<tr><td><strong>Friendlier SQL<\/strong><\/td><td><code>$variable<\/code> shorthand, <code>lambda x: ...<\/code> syntax, JSON edit functions like <code>json_set<\/code>, nested schemas and more.<\/td><\/tr>\n<\/tbody><\/table><\/div>\n<div class=\"dd-note\"><strong>Version check:<\/strong> at the time of writing, <code>pip install duckdb<\/code> gives <strong>2.0.0<\/strong> and <code>npm install @duckdb\/node-api<\/code> gives <strong>2.0.0-r.1<\/strong>. DuckDB 1.4 is supported until 17 November and 1.5 until March next year.<\/div>\n<\/div>\n\n\n\n<div class=\"dd-post\">\n<h2 id=\"install\">Install DuckDB<\/h2>\n<p>Pick your language. You do not need both \u2013 the SQL is exactly the same.<\/p>\n<div class=\"dd-vsc dd-vsc-termonly\" role=\"group\" aria-label=\"Terminal: Terminal \u2014 install DuckDB\">\n<div class=\"dd-vsc-bar\"><span class=\"dd-dots\"><i><\/i><i><\/i><i><\/i><\/span><span class=\"dd-vsc-title\">Terminal \u2014 install DuckDB<\/span><\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"l\"><span class=\"c\"># Python (DuckDB 2.0 needs Python 3.11 or newer)<\/span><\/span>\n<span class=\"l\"><span class=\"gp\">$<\/span> python3 --version<\/span>\n<span class=\"l\"><span class=\"gp\">$<\/span> pip install duckdb<\/span>\n<span class=\"l\"><span class=\"gp\">$<\/span> python3 -c \"import duckdb; print(duckdb.__version__)\"<\/span>\n<span class=\"l\">2.0.0<\/span>\n<span class=\"l\"><span class=\"c\"><\/span><\/span>\n<span class=\"l\"><span class=\"c\"># Node.js (the modern \"Node Neo\" client)<\/span><\/span>\n<span class=\"l\"><span class=\"gp\">$<\/span> npm install @duckdb\/node-api<\/span>\n<span class=\"l\"><span class=\"c\"><\/span><\/span>\n<span class=\"l\"><span class=\"c\"># Optional: the command-line shell (macOS \/ Linux)<\/span><\/span>\n<span class=\"l\"><span class=\"gp\">$<\/span> curl https:\/\/install.duckdb.org | sh<\/span><\/code><\/pre>\n<\/div>\n<div class=\"dd-warn\"><strong>On Python 3.10 or older?<\/strong> <code>pip<\/code> will quietly install an old 1.x version. Upgrade Python to 3.11+ (or use <code>uv<\/code> \/ <code>pyenv<\/code>) to get DuckDB 2.0.<\/div>\n\n<h2 id=\"first-query\">Your first query on a CSV file<\/h2>\n<p>We will use a small <code>sales.csv<\/code> file with 1,000 orders from five Indian cities (columns: <code>order_id, order_date, city, product, qty, price<\/code>). Create any similar CSV, or generate one with a few lines of Python. Now ask a real business question: <em>which city brings the most revenue?<\/em><\/p>\n<div class=\"dd-vsc\" role=\"group\" aria-label=\"Code editor: quickstart.py, quickstart.mjs, sales.csv\">\n<input type=\"radio\" name=\"v1\" id=\"v1f1\" class=\"f1\" checked>\n<input type=\"radio\" name=\"v1\" id=\"v1f2\" class=\"f2\">\n<input type=\"radio\" name=\"v1\" id=\"v1f3\" class=\"f3\">\n<input type=\"checkbox\" id=\"v1r1\" class=\"r1\">\n<input type=\"checkbox\" id=\"v1r2\" class=\"r2\">\n<div class=\"dd-vsc-bar\"><span class=\"dd-dots\"><i><\/i><i><\/i><i><\/i><\/span><span class=\"dd-vsc-title\">quickstart.py \u2014 duckdb-demo \u2014 Visual Studio Code<\/span><\/div>\n<div class=\"dd-vsc-tabs\">\n<label for=\"v1f1\" class=\"t1\"><b class=\"ic py\">PY<\/b>quickstart.py<\/label>\n<label for=\"v1f2\" class=\"t2\"><b class=\"ic js\">JS<\/b>quickstart.mjs<\/label>\n<label for=\"v1f3\" class=\"t3\"><b class=\"ic csv\">CSV<\/b>sales.csv<\/label>\n<label for=\"v1r1\" class=\"run run1\" title=\"Show the output in the terminal\">\u25b6 Run<\/label>\n<label for=\"v1r2\" class=\"run run2\" title=\"Show the output in the terminal\">\u25b6 Run<\/label>\n<\/div>\n<div class=\"dd-vsc-pane p1\">\n<div class=\"dd-vsc-crumbs\">duckdb-demo \u203a quickstart.py<\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"l\"><span class=\"kn\">import<\/span> <span class=\"n\">duckdb<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"nb\">print<\/span>(<span class=\"s\">\"DuckDB version:\"<\/span>, <span class=\"n\">duckdb<\/span>.<span class=\"n\">__version__<\/span>)<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"c\"># Query a CSV file directly - no import step, no server<\/span><\/span>\n<span class=\"l\"><span class=\"n\">result<\/span> = <span class=\"n\">duckdb<\/span>.<span class=\"nf\">sql<\/span>(<span class=\"s\">\"\"\"<\/span><\/span>\n<span class=\"l\"><span class=\"s\">    SELECT city,<\/span><\/span>\n<span class=\"l\"><span class=\"s\">           count(*)              AS orders,<\/span><\/span>\n<span class=\"l\"><span class=\"s\">           sum(qty * price)      AS revenue<\/span><\/span>\n<span class=\"l\"><span class=\"s\">    FROM 'sales.csv'<\/span><\/span>\n<span class=\"l\"><span class=\"s\">    GROUP BY city<\/span><\/span>\n<span class=\"l\"><span class=\"s\">    ORDER BY revenue DESC<\/span><\/span>\n<span class=\"l\"><span class=\"s\">\"\"\"<\/span>)<\/span>\n<span class=\"l\"><span class=\"n\">result<\/span>.<span class=\"nf\">show<\/span>()<\/span><\/code><\/pre>\n<div class=\"dd-vsc-term\"><div class=\"dd-term-head\"><span>TERMINAL<\/span><span>OUTPUT<\/span><span>PROBLEMS<\/span><span class=\"dd-term-note\">Real output (DuckDB 2.0.0, Linux)<\/span><\/div><pre><code><span class=\"gp\">$<\/span> python3 quickstart.py\nDuckDB version: 2.0.0\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502   city    \u2502 orders \u2502 revenue \u2502\n\u2502  varchar  \u2502 int64  \u2502 int128  \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Bengaluru \u2502    208 \u2502 9048500 \u2502\n\u2502 Mumbai    \u2502    215 \u2502 8241000 \u2502\n\u2502 Pune      \u2502    194 \u2502 7765000 \u2502\n\u2502 Chennai   \u2502    179 \u2502 7343500 \u2502\n\u2502 Delhi     \u2502    204 \u2502 6381500 \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518<\/code><\/pre><\/div>\n<div class=\"dd-vsc-status\"><span>\u2387 main<\/span><span>Ln 14, Col 1<\/span><span class=\"sp\"><\/span><span>Spaces: 4<\/span><span>UTF-8<\/span><span>LF<\/span><span>Python<\/span><\/div>\n<\/div>\n<div class=\"dd-vsc-pane p2\">\n<div class=\"dd-vsc-crumbs\">duckdb-demo \u203a quickstart.mjs<\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"l\"><span class=\"k\">import<\/span> { <span class=\"n\">DuckDBInstance<\/span> } <span class=\"k\">from<\/span> <span class=\"s\">'@duckdb\/node-api'<\/span>;<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"c\">\/\/ In-memory database (pass a file name to save to disk)<\/span><\/span>\n<span class=\"l\"><span class=\"k\">const<\/span> <span class=\"n\">instance<\/span> = <span class=\"k\">await<\/span> <span class=\"n\">DuckDBInstance<\/span>.<span class=\"nf\">create<\/span>(<span class=\"s\">':memory:'<\/span>);<\/span>\n<span class=\"l\"><span class=\"k\">const<\/span> <span class=\"n\">connection<\/span> = <span class=\"k\">await<\/span> <span class=\"n\">instance<\/span>.<span class=\"nf\">connect<\/span>();<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"k\">const<\/span> <span class=\"n\">reader<\/span> = <span class=\"k\">await<\/span> <span class=\"n\">connection<\/span>.<span class=\"nf\">runAndReadAll<\/span>(<span class=\"s\">`<\/span><\/span>\n<span class=\"l\"><span class=\"s\">  SELECT city,<\/span><\/span>\n<span class=\"l\"><span class=\"s\">         count(*)         AS orders,<\/span><\/span>\n<span class=\"l\"><span class=\"s\">         sum(qty * price) AS revenue<\/span><\/span>\n<span class=\"l\"><span class=\"s\">  FROM 'sales.csv'<\/span><\/span>\n<span class=\"l\"><span class=\"s\">  GROUP BY city<\/span><\/span>\n<span class=\"l\"><span class=\"s\">  ORDER BY revenue DESC<\/span><\/span>\n<span class=\"l\"><span class=\"s\">`<\/span>);<\/span>\n<span class=\"l\"><span class=\"nb\">console<\/span>.<span class=\"nf\">table<\/span>(<span class=\"n\">reader<\/span>.<span class=\"nf\">getRowObjectsJson<\/span>());<\/span><\/code><\/pre>\n<div class=\"dd-vsc-term\"><div class=\"dd-term-head\"><span>TERMINAL<\/span><span>OUTPUT<\/span><span>PROBLEMS<\/span><span class=\"dd-term-note\">Real output (DuckDB 2.0.0, Node.js 22)<\/span><\/div><pre><code><span class=\"gp\">$<\/span> node quickstart.mjs\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 (index) \u2502 city        \u2502 orders \u2502 revenue   \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 0       \u2502 'Bengaluru' \u2502 '208'  \u2502 '9048500' \u2502\n\u2502 1       \u2502 'Mumbai'    \u2502 '215'  \u2502 '8241000' \u2502\n\u2502 2       \u2502 'Pune'      \u2502 '194'  \u2502 '7765000' \u2502\n\u2502 3       \u2502 'Chennai'   \u2502 '179'  \u2502 '7343500' \u2502\n\u2502 4       \u2502 'Delhi'     \u2502 '204'  \u2502 '6381500' \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518<\/code><\/pre><\/div>\n<div class=\"dd-vsc-status\"><span>\u2387 main<\/span><span>Ln 15, Col 1<\/span><span class=\"sp\"><\/span><span>Spaces: 2<\/span><span>UTF-8<\/span><span>LF<\/span><span>JavaScript<\/span><\/div>\n<\/div>\n<div class=\"dd-vsc-pane p3\">\n<div class=\"dd-vsc-crumbs\">duckdb-demo \u203a sales.csv<\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"l\">order_id,order_date,city,product,qty,price<\/span>\n<span class=\"l\">1,2026-07-13,Delhi,Headphones,1,2500<\/span>\n<span class=\"l\">2,2026-07-12,Chennai,Monitor,1,12000<\/span>\n<span class=\"l\">3,2026-07-02,Delhi,Monitor,2,12000<\/span>\n<span class=\"l\">4,2026-07-08,Delhi,Keyboard,3,1500<\/span>\n<span class=\"l\">...<\/span><\/code><\/pre>\n<div class=\"dd-vsc-status\"><span>\u2387 main<\/span><span>Ln 6, Col 1<\/span><span class=\"sp\"><\/span><span>Spaces: 4<\/span><span>UTF-8<\/span><span>LF<\/span><span>CSV<\/span><\/div>\n<\/div>\n<\/div>\n<p class=\"dd-vsc-hint\">Click the tabs to switch between Python, Node.js and the data file, then click <strong>\u25b6 Run<\/strong> to see the real output.<\/p>\n<p>Notice what we did <strong>not<\/strong> do: no <code>CREATE TABLE<\/code>, no import, no column types. DuckDB looked at the file, detected the columns and types, and ran the query. A few things to note:<\/p>\n<ul>\n<li><code>duckdb.sql()<\/code> uses a default in-memory database. <code>.show()<\/code> prints a nice table.<\/li>\n<li>The revenue column is <code>int128<\/code> (DuckDB&#8217;s <code>HUGEINT<\/code>) because summing many <code>BIGINT<\/code> values could overflow.<\/li>\n<li>In Node.js, <code>getRowObjectsJson()<\/code> turns big integers into strings so they are safe to send as JSON from an API.<\/li>\n<\/ul>\n<figure>\n<svg viewBox=\"0 0 900 200\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" role=\"img\" aria-label=\"Typical DuckDB workflow: raw CSV, query with SQL, save to a DuckDB file or Parquet, then use the result in pandas or a chart\">\n<rect width=\"900\" height=\"200\" fill=\"#0b1020\"\/>\n<g font-family=\"Segoe UI,Arial,sans-serif\" font-weight=\"700\" font-size=\"16\">\n<rect x=\"30\" y=\"70\" width=\"170\" height=\"64\" rx=\"12\" fill=\"#151b3b\" stroke=\"#89e051\" stroke-width=\"2\"\/><text x=\"115\" y=\"100\" text-anchor=\"middle\" fill=\"#d8ffd0\">1. Raw files<\/text><text x=\"115\" y=\"122\" text-anchor=\"middle\" font-size=\"13\" font-weight=\"400\" fill=\"#aab4e8\">CSV \/ JSON \/ Parquet<\/text>\n<rect x=\"250\" y=\"70\" width=\"170\" height=\"64\" rx=\"12\" fill=\"url(#ddac3)\"\/><text x=\"335\" y=\"100\" text-anchor=\"middle\" fill=\"#fff\">2. Query with SQL<\/text><text x=\"335\" y=\"122\" text-anchor=\"middle\" font-size=\"13\" font-weight=\"400\" fill=\"#e8ebff\">duckdb.sql(&#8230;)<\/text>\n<rect x=\"470\" y=\"70\" width=\"170\" height=\"64\" rx=\"12\" fill=\"#151b3b\" stroke=\"#3178c6\" stroke-width=\"2\"\/><text x=\"555\" y=\"100\" text-anchor=\"middle\" fill=\"#c9d1ff\">3. Save<\/text><text x=\"555\" y=\"122\" text-anchor=\"middle\" font-size=\"13\" font-weight=\"400\" fill=\"#aab4e8\">shop.duckdb \/ .parquet<\/text>\n<rect x=\"690\" y=\"70\" width=\"180\" height=\"64\" rx=\"12\" fill=\"#151b3b\" stroke=\"#7c5cff\" stroke-width=\"2\"\/><text x=\"780\" y=\"100\" text-anchor=\"middle\" fill=\"#e0d8ff\">4. Use the result<\/text><text x=\"780\" y=\"122\" text-anchor=\"middle\" font-size=\"13\" font-weight=\"400\" fill=\"#aab4e8\">pandas \/ API \/ chart<\/text>\n<\/g>\n<defs><linearGradient id=\"ddac3\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"0\"><stop offset=\"0\" stop-color=\"#3178c6\"\/><stop offset=\"1\" stop-color=\"#7c5cff\"\/><\/linearGradient><\/defs>\n<path id=\"ddfl\" d=\"M200 102H250M420 102H470M640 102H690\" stroke=\"#ffd43b\" stroke-width=\"3\" fill=\"none\"\/>\n<path id=\"ddfl2\" d=\"M200 102H690\" fill=\"none\" stroke=\"none\"\/>\n<circle r=\"6\" fill=\"#ffd43b\"><animateMotion dur=\"4s\" repeatCount=\"indefinite\"><mpath href=\"#ddfl2\"\/><\/animateMotion><\/circle>\n<text x=\"450\" y=\"178\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#aab4e8\">Everything happens inside one Python or Node.js process<\/text>\n<\/svg>\n<figcaption>Figure 3: The workflow you will build in this tutorial.<\/figcaption>\n<\/figure>\n\n<h2 id=\"save\">Save to a database file and Parquet<\/h2>\n<p>In-memory is great for quick analysis. For real projects you usually want to keep the data. Pass a file name to <code>duckdb.connect()<\/code> and everything you create is stored in that single file. We will also export the table to <strong>Parquet<\/strong> \u2013 a compressed, column-based file format used by Spark, pandas, BigQuery and most data tools.<\/p>\n<div class=\"dd-vsc\" role=\"group\" aria-label=\"Code editor: save_db.py\">\n<input type=\"radio\" name=\"v2\" id=\"v2f1\" class=\"f1\" checked>\n<input type=\"checkbox\" id=\"v2r1\" class=\"r1\">\n<div class=\"dd-vsc-bar\"><span class=\"dd-dots\"><i><\/i><i><\/i><i><\/i><\/span><span class=\"dd-vsc-title\">save_db.py \u2014 duckdb-demo \u2014 Visual Studio Code<\/span><\/div>\n<div class=\"dd-vsc-tabs\">\n<label for=\"v2f1\" class=\"t1\"><b class=\"ic py\">PY<\/b>save_db.py<\/label>\n<label for=\"v2r1\" class=\"run run1\" title=\"Show the output in the terminal\">\u25b6 Run<\/label>\n<\/div>\n<div class=\"dd-vsc-pane p1\">\n<div class=\"dd-vsc-crumbs\">duckdb-demo \u203a save_db.py<\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"l\"><span class=\"kn\">import<\/span> <span class=\"n\">duckdb<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"c\"># Open (or create) a database file on disk<\/span><\/span>\n<span class=\"l\"><span class=\"n\">con<\/span> = <span class=\"n\">duckdb<\/span>.<span class=\"nf\">connect<\/span>(<span class=\"s\">\"shop.duckdb\"<\/span>)<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"c\"># Load the CSV into a real table (types are detected automatically)<\/span><\/span>\n<span class=\"l\"><span class=\"c\"># Tip: use execute() for statements that change data (DDL \/ DML)<\/span><\/span>\n<span class=\"l\"><span class=\"n\">con<\/span>.<span class=\"nf\">execute<\/span>(<span class=\"s\">\"CREATE OR REPLACE TABLE sales AS FROM 'sales.csv'\"<\/span>)<\/span>\n<span class=\"l\"><span class=\"n\">con<\/span>.<span class=\"nf\">sql<\/span>(<span class=\"s\">\"SELECT column_name, column_type FROM (DESCRIBE sales)\"<\/span>).<span class=\"nf\">show<\/span>()<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"c\"># Export to Parquet - a compressed, column-based file format<\/span><\/span>\n<span class=\"l\"><span class=\"n\">con<\/span>.<span class=\"nf\">execute<\/span>(<span class=\"s\">\"COPY sales TO 'sales.parquet' (FORMAT parquet)\"<\/span>)<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"c\"># Read the Parquet file back and get monthly revenue<\/span><\/span>\n<span class=\"l\"><span class=\"n\">con<\/span>.<span class=\"nf\">sql<\/span>(<span class=\"s\">\"\"\"<\/span><\/span>\n<span class=\"l\"><span class=\"s\">    SELECT strftime(order_date, '%Y-%m') AS month,<\/span><\/span>\n<span class=\"l\"><span class=\"s\">           sum(qty * price)              AS revenue<\/span><\/span>\n<span class=\"l\"><span class=\"s\">    FROM 'sales.parquet'<\/span><\/span>\n<span class=\"l\"><span class=\"s\">    GROUP BY ALL<\/span><\/span>\n<span class=\"l\"><span class=\"s\">    ORDER BY month<\/span><\/span>\n<span class=\"l\"><span class=\"s\">\"\"\"<\/span>).<span class=\"nf\">show<\/span>()<\/span>\n<span class=\"l\"><span class=\"n\">con<\/span>.<span class=\"nf\">close<\/span>()<\/span><\/code><\/pre>\n<div class=\"dd-vsc-term\"><div class=\"dd-term-head\"><span>TERMINAL<\/span><span>OUTPUT<\/span><span>PROBLEMS<\/span><span class=\"dd-term-note\">Real output (DuckDB 2.0.0, Linux)<\/span><\/div><pre><code><span class=\"gp\">$<\/span> python3 save_db.py\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 column_name \u2502 column_type \u2502\n\u2502   varchar   \u2502   varchar   \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 order_id    \u2502 BIGINT      \u2502\n\u2502 order_date  \u2502 DATE        \u2502\n\u2502 city        \u2502 VARCHAR     \u2502\n\u2502 product     \u2502 VARCHAR     \u2502\n\u2502 qty         \u2502 BIGINT      \u2502\n\u2502 price       \u2502 BIGINT      \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502  month  \u2502 revenue  \u2502\n\u2502 varchar \u2502  int128  \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 2026-07 \u2502 13404500 \u2502\n\u2502 2026-08 \u2502 13935000 \u2502\n\u2502 2026-09 \u2502 11440000 \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518<\/code><\/pre><\/div>\n<div class=\"dd-vsc-status\"><span>\u2387 main<\/span><span>Ln 22, Col 1<\/span><span class=\"sp\"><\/span><span>Spaces: 4<\/span><span>UTF-8<\/span><span>LF<\/span><span>Python<\/span><\/div>\n<\/div>\n<\/div>\n<p class=\"dd-vsc-hint\">Click <strong>\u25b6 Run<\/strong> to see the detected column types and the monthly revenue.<\/p>\n<ul>\n<li><code>CREATE TABLE sales AS FROM 'sales.csv'<\/code> uses DuckDB&#8217;s &#8220;friendly SQL&#8221;: you can skip <code>SELECT *<\/code>.<\/li>\n<li><code>GROUP BY ALL<\/code> groups by every column that is not an aggregate \u2013 no need to repeat <code>month<\/code>.<\/li>\n<li>On our machine the 38,890-byte CSV became an 8,199-byte Parquet file \u2013 about 4.7 times smaller.<\/li>\n<li>We used <code>con.execute()<\/code> for <code>CREATE<\/code> and <code>COPY<\/code>. That is the recommended style in DuckDB 2.0 for statements that change data.<\/li>\n<\/ul>\n\n<h2 id=\"pandas\">DuckDB with pandas<\/h2>\n<p>This is the feature that makes data scientists fall in love with DuckDB: it can query a pandas DataFrame <strong>by its variable name<\/strong> and join it with files on disk. The result comes back as a DataFrame with <code>.df()<\/code>.<\/p>\n<div class=\"dd-vsc\" role=\"group\" aria-label=\"Code editor: pandas_demo.py\">\n<input type=\"radio\" name=\"v3\" id=\"v3f1\" class=\"f1\" checked>\n<input type=\"checkbox\" id=\"v3r1\" class=\"r1\">\n<div class=\"dd-vsc-bar\"><span class=\"dd-dots\"><i><\/i><i><\/i><i><\/i><\/span><span class=\"dd-vsc-title\">pandas_demo.py \u2014 duckdb-demo \u2014 Visual Studio Code<\/span><\/div>\n<div class=\"dd-vsc-tabs\">\n<label for=\"v3f1\" class=\"t1\"><b class=\"ic py\">PY<\/b>pandas_demo.py<\/label>\n<label for=\"v3r1\" class=\"run run1\" title=\"Show the output in the terminal\">\u25b6 Run<\/label>\n<\/div>\n<div class=\"dd-vsc-pane p1\">\n<div class=\"dd-vsc-crumbs\">duckdb-demo \u203a pandas_demo.py<\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"l\"><span class=\"kn\">import<\/span> <span class=\"n\">duckdb<\/span><\/span>\n<span class=\"l\"><span class=\"kn\">import<\/span> <span class=\"n\">pandas<\/span> <span class=\"k\">as<\/span> <span class=\"n\">pd<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"c\"># A normal pandas DataFrame in memory<\/span><\/span>\n<span class=\"l\"><span class=\"n\">targets<\/span> = <span class=\"n\">pd<\/span>.<span class=\"nf\">DataFrame<\/span>({<\/span>\n<span class=\"l\">    <span class=\"s\">\"city\"<\/span>:   [<span class=\"s\">\"Delhi\"<\/span>, <span class=\"s\">\"Mumbai\"<\/span>, <span class=\"s\">\"Bengaluru\"<\/span>, <span class=\"s\">\"Pune\"<\/span>, <span class=\"s\">\"Chennai\"<\/span>],<\/span>\n<span class=\"l\">    <span class=\"s\">\"target\"<\/span>: [<span class=\"m\">7_000_000<\/span>, <span class=\"m\">8_000_000<\/span>, <span class=\"m\">9_000_000<\/span>, <span class=\"m\">7_500_000<\/span>, <span class=\"m\">7_000_000<\/span>],<\/span>\n<span class=\"l\">})<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"c\"># DuckDB can query the DataFrame by its variable name<\/span><\/span>\n<span class=\"l\"><span class=\"c\"># and JOIN it with a file on disk<\/span><\/span>\n<span class=\"l\"><span class=\"n\">report<\/span> = <span class=\"n\">duckdb<\/span>.<span class=\"nf\">sql<\/span>(<span class=\"s\">\"\"\"<\/span><\/span>\n<span class=\"l\"><span class=\"s\">    SELECT t.city,<\/span><\/span>\n<span class=\"l\"><span class=\"s\">           sum(s.qty * s.price)              AS revenue,<\/span><\/span>\n<span class=\"l\"><span class=\"s\">           t.target,<\/span><\/span>\n<span class=\"l\"><span class=\"s\">           revenue &gt;= t.target               AS hit_target<\/span><\/span>\n<span class=\"l\"><span class=\"s\">    FROM 'sales.csv' AS s<\/span><\/span>\n<span class=\"l\"><span class=\"s\">    JOIN targets   AS t USING (city)<\/span><\/span>\n<span class=\"l\"><span class=\"s\">    GROUP BY t.city, t.target<\/span><\/span>\n<span class=\"l\"><span class=\"s\">    ORDER BY revenue DESC<\/span><\/span>\n<span class=\"l\"><span class=\"s\">\"\"\"<\/span>).<span class=\"nf\">df<\/span>()          <span class=\"c\"># back to a pandas DataFrame<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"nb\">print<\/span>(<span class=\"n\">report<\/span>)<\/span><\/code><\/pre>\n<div class=\"dd-vsc-term\"><div class=\"dd-term-head\"><span>TERMINAL<\/span><span>OUTPUT<\/span><span>PROBLEMS<\/span><span class=\"dd-term-note\">Real output (DuckDB 2.0.0, pandas 3.0)<\/span><\/div><pre><code><span class=\"gp\">$<\/span> python3 pandas_demo.py\n        city    revenue   target  hit_target\n0  Bengaluru  9048500.0  9000000        True\n1     Mumbai  8241000.0  8000000        True\n2       Pune  7765000.0  7500000        True\n3    Chennai  7343500.0  7000000        True\n4      Delhi  6381500.0  7000000       False<\/code><\/pre><\/div>\n<div class=\"dd-vsc-status\"><span>\u2387 main<\/span><span>Ln 23, Col 1<\/span><span class=\"sp\"><\/span><span>Spaces: 4<\/span><span>UTF-8<\/span><span>LF<\/span><span>Python<\/span><\/div>\n<\/div>\n<\/div>\n<p class=\"dd-vsc-hint\">Click <strong>\u25b6 Run<\/strong> to see which cities hit their sales target.<\/p>\n<div class=\"dd-tip\"><strong>Tip:<\/strong> You can also return Polars (<code>.pl()<\/code>) or Arrow (<code>.to_arrow_table()<\/code>). Mixing pandas for small tables and DuckDB for heavy SQL is a very common, very productive pattern.<\/div>\n<\/div>\n\n\n\n<div class=\"dd-post\">\n<h2 id=\"speed\">How fast is it?<\/h2>\n<p>Let&#8217;s create 5 million rows inside DuckDB and run an aggregation. <code>range(5_000_000)<\/code> is a table function that generates numbers, so we don&#8217;t need any file.<\/p>\n<div class=\"dd-vsc\" role=\"group\" aria-label=\"Code editor: big_data.py\">\n<input type=\"radio\" name=\"v4\" id=\"v4f1\" class=\"f1\" checked>\n<input type=\"checkbox\" id=\"v4r1\" class=\"r1\">\n<div class=\"dd-vsc-bar\"><span class=\"dd-dots\"><i><\/i><i><\/i><i><\/i><\/span><span class=\"dd-vsc-title\">big_data.py \u2014 duckdb-demo \u2014 Visual Studio Code<\/span><\/div>\n<div class=\"dd-vsc-tabs\">\n<label for=\"v4f1\" class=\"t1\"><b class=\"ic py\">PY<\/b>big_data.py<\/label>\n<label for=\"v4r1\" class=\"run run1\" title=\"Show the output in the terminal\">\u25b6 Run<\/label>\n<\/div>\n<div class=\"dd-vsc-pane p1\">\n<div class=\"dd-vsc-crumbs\">duckdb-demo \u203a big_data.py<\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"l\"><span class=\"kn\">import<\/span> <span class=\"n\">time<\/span><\/span>\n<span class=\"l\"><span class=\"kn\">import<\/span> <span class=\"n\">duckdb<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">con<\/span> = <span class=\"n\">duckdb<\/span>.<span class=\"nf\">connect<\/span>()<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"c\"># Generate 5 million fake page-view rows inside DuckDB<\/span><\/span>\n<span class=\"l\"><span class=\"n\">con<\/span>.<span class=\"nf\">execute<\/span>(<span class=\"s\">\"\"\"<\/span><\/span>\n<span class=\"l\"><span class=\"s\">    CREATE TABLE views AS<\/span><\/span>\n<span class=\"l\"><span class=\"s\">    SELECT range                         AS id,<\/span><\/span>\n<span class=\"l\"><span class=\"s\">           range % 1000                  AS page_id,<\/span><\/span>\n<span class=\"l\"><span class=\"s\">           (random() * 300)::INT         AS seconds_on_page<\/span><\/span>\n<span class=\"l\"><span class=\"s\">    FROM range(5_000_000)<\/span><\/span>\n<span class=\"l\"><span class=\"s\">\"\"\"<\/span>)<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">start<\/span> = <span class=\"n\">time<\/span>.<span class=\"nf\">perf_counter<\/span>()<\/span>\n<span class=\"l\"><span class=\"n\">top<\/span> = <span class=\"n\">con<\/span>.<span class=\"nf\">execute<\/span>(<span class=\"s\">\"\"\"<\/span><\/span>\n<span class=\"l\"><span class=\"s\">    SELECT page_id, avg(seconds_on_page) AS avg_seconds<\/span><\/span>\n<span class=\"l\"><span class=\"s\">    FROM views<\/span><\/span>\n<span class=\"l\"><span class=\"s\">    GROUP BY page_id<\/span><\/span>\n<span class=\"l\"><span class=\"s\">    ORDER BY avg_seconds DESC<\/span><\/span>\n<span class=\"l\"><span class=\"s\">    LIMIT 3<\/span><\/span>\n<span class=\"l\"><span class=\"s\">\"\"\"<\/span>).<span class=\"nf\">fetchall<\/span>()<\/span>\n<span class=\"l\"><span class=\"n\">elapsed<\/span> = <span class=\"n\">time<\/span>.<span class=\"nf\">perf_counter<\/span>() - <span class=\"n\">start<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"nb\">print<\/span>(<span class=\"s\">\"rows:\"<\/span>, <span class=\"n\">con<\/span>.<span class=\"nf\">execute<\/span>(<span class=\"s\">\"SELECT count(*) FROM views\"<\/span>).<span class=\"nf\">fetchone<\/span>()[<span class=\"m\">0<\/span>])<\/span>\n<span class=\"l\"><span class=\"nb\">print<\/span>(<span class=\"s\">\"top pages fetched:\"<\/span>, <span class=\"nb\">len<\/span>(<span class=\"n\">top<\/span>))<\/span>\n<span class=\"l\"><span class=\"nb\">print<\/span>(<span class=\"s\">f\"query time: {<\/span><span class=\"n\">elapsed<\/span><span class=\"s\">:.3f} s\"<\/span>)<\/span><\/code><\/pre>\n<div class=\"dd-vsc-term\"><div class=\"dd-term-head\"><span>TERMINAL<\/span><span>OUTPUT<\/span><span>PROBLEMS<\/span><span class=\"dd-term-note\">Real output \u2013 2-core cloud Linux VM; your time will differ<\/span><\/div><pre><code><span class=\"gp\">$<\/span> python3 big_data.py\nrows: 5000000\ntop pages fetched: 3\nquery time: 0.025 s<\/code><\/pre><\/div>\n<div class=\"dd-vsc-status\"><span>\u2387 main<\/span><span>Ln 27, Col 1<\/span><span class=\"sp\"><\/span><span>Spaces: 4<\/span><span>UTF-8<\/span><span>LF<\/span><span>Python<\/span><\/div>\n<\/div>\n<\/div>\n<p class=\"dd-vsc-hint\">Click <strong>\u25b6 Run<\/strong> to see the real timing from our small 2-core test machine.<\/p>\n<p>Grouping 5 million rows took about 25 milliseconds on a small 2-core cloud machine. Your number will be different, but the point is clear: for analytics on a laptop, DuckDB is often faster than setting up a &#8220;big data&#8221; cluster. For files bigger than your RAM, DuckDB can spill to disk, so it does not simply crash.<\/p>\n\n<h2 id=\"v2-features\">Try the new DuckDB 2.0 features<\/h2>\n<p>Now let&#8217;s try four changes that are new in 2.0: the <code>VARIANT<\/code> type, the new lambda syntax, the <code>$variable<\/code> shorthand and stricter division by zero.<\/p>\n<div class=\"dd-vsc\" role=\"group\" aria-label=\"Code editor: new_in_v2.py\">\n<input type=\"radio\" name=\"v5\" id=\"v5f1\" class=\"f1\" checked>\n<input type=\"checkbox\" id=\"v5r1\" class=\"r1\">\n<div class=\"dd-vsc-bar\"><span class=\"dd-dots\"><i><\/i><i><\/i><i><\/i><\/span><span class=\"dd-vsc-title\">new_in_v2.py \u2014 duckdb-demo \u2014 Visual Studio Code<\/span><\/div>\n<div class=\"dd-vsc-tabs\">\n<label for=\"v5f1\" class=\"t1\"><b class=\"ic py\">PY<\/b>new_in_v2.py<\/label>\n<label for=\"v5r1\" class=\"run run1\" title=\"Show the output in the terminal\">\u25b6 Run<\/label>\n<\/div>\n<div class=\"dd-vsc-pane p1\">\n<div class=\"dd-vsc-crumbs\">duckdb-demo \u203a new_in_v2.py<\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"l\"><span class=\"kn\">import<\/span> <span class=\"n\">duckdb<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">con<\/span> = <span class=\"n\">duckdb<\/span>.<span class=\"nf\">connect<\/span>()<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"c\"># 1) VARIANT: store mixed, schema-less data (like JSON, but faster)<\/span><\/span>\n<span class=\"l\"><span class=\"n\">con<\/span>.<span class=\"nf\">execute<\/span>(<span class=\"s\">\"CREATE TABLE events (payload VARIANT)\"<\/span>)<\/span>\n<span class=\"l\"><span class=\"n\">con<\/span>.<span class=\"nf\">execute<\/span>(<span class=\"s\">\"\"\"<\/span><\/span>\n<span class=\"l\"><span class=\"s\">    INSERT INTO events VALUES<\/span><\/span>\n<span class=\"l\"><span class=\"s\">      ('{\"user\": {\"id\": 42, \"tags\": [\"a\", \"b\"]}}'::JSON::VARIANT),<\/span><\/span>\n<span class=\"l\"><span class=\"s\">      ('{\"event\": \"click\", \"x\": 10, \"y\": 20}'::JSON::VARIANT)<\/span><\/span>\n<span class=\"l\"><span class=\"s\">\"\"\"<\/span>)<\/span>\n<span class=\"l\"><span class=\"n\">con<\/span>.<span class=\"nf\">sql<\/span>(<span class=\"s\">\"\"\"SELECT variant_type(payload) AS type,<\/span><\/span>\n<span class=\"l\"><span class=\"s\">                  variant_keys(payload) AS keys<\/span><\/span>\n<span class=\"l\"><span class=\"s\">           FROM events\"\"\"<\/span>).<span class=\"nf\">show<\/span>()<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"c\"># 2) New lambda syntax:  lambda x: ...   (old  x -&gt; ...  is off by default)<\/span><\/span>\n<span class=\"l\"><span class=\"n\">tens<\/span> = <span class=\"n\">con<\/span>.<span class=\"nf\">execute<\/span>(<span class=\"s\">\"SELECT list_transform([1, 2, 3], lambda x: x * 10)\"<\/span>)<\/span>\n<span class=\"l\"><span class=\"nb\">print<\/span>(<span class=\"n\">tens<\/span>.<span class=\"nf\">fetchone<\/span>())<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"c\"># 3) $variable shorthand instead of getvariable('min_qty')<\/span><\/span>\n<span class=\"l\"><span class=\"n\">con<\/span>.<span class=\"nf\">execute<\/span>(<span class=\"s\">\"SET VARIABLE min_qty = 3\"<\/span>)<\/span>\n<span class=\"l\"><span class=\"n\">big<\/span> = <span class=\"n\">con<\/span>.<span class=\"nf\">execute<\/span>(<span class=\"s\">\"SELECT count(*) FROM 'sales.csv' WHERE qty &gt;= $min_qty\"<\/span>)<\/span>\n<span class=\"l\"><span class=\"nb\">print<\/span>(<span class=\"n\">big<\/span>.<span class=\"nf\">fetchone<\/span>())<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"c\"># 4) Integer division by zero is now an error (TRY() gives NULL instead)<\/span><\/span>\n<span class=\"l\"><span class=\"nb\">print<\/span>(<span class=\"n\">con<\/span>.<span class=\"nf\">execute<\/span>(<span class=\"s\">\"SELECT TRY(10 \/\/ 0)\"<\/span>).<span class=\"nf\">fetchone<\/span>())<\/span>\n<span class=\"l\"><span class=\"k\">try<\/span>:<\/span>\n<span class=\"l\">    <span class=\"n\">con<\/span>.<span class=\"nf\">execute<\/span>(<span class=\"s\">\"SELECT 10 \/\/ 0\"<\/span>).<span class=\"nf\">fetchone<\/span>()<\/span>\n<span class=\"l\"><span class=\"k\">except<\/span> <span class=\"n\">duckdb<\/span>.<span class=\"n\">Error<\/span> <span class=\"k\">as<\/span> <span class=\"n\">e<\/span>:<\/span>\n<span class=\"l\">    <span class=\"nb\">print<\/span>(<span class=\"s\">\"Error:\"<\/span>, <span class=\"nb\">type<\/span>(<span class=\"n\">e<\/span>).<span class=\"n\">__name__<\/span>)<\/span><\/code><\/pre>\n<div class=\"dd-vsc-term\"><div class=\"dd-term-head\"><span>TERMINAL<\/span><span>OUTPUT<\/span><span>PROBLEMS<\/span><span class=\"dd-term-note\">Real output (DuckDB 2.0.0, Linux)<\/span><\/div><pre><code><span class=\"gp\">$<\/span> python3 new_in_v2.py\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502  type   \u2502     keys      \u2502\n\u2502 varchar \u2502   varchar[]   \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 OBJECT  \u2502 [user]        \u2502\n\u2502 OBJECT  \u2502 [event, x, y] \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n\n([10, 20, 30],)\n(343,)\n(None,)\nError: InvalidInputException<\/code><\/pre><\/div>\n<div class=\"dd-vsc-status\"><span>\u2387 main<\/span><span>Ln 30, Col 1<\/span><span class=\"sp\"><\/span><span>Spaces: 4<\/span><span>UTF-8<\/span><span>LF<\/span><span>Python<\/span><\/div>\n<\/div>\n<\/div>\n<p class=\"dd-vsc-hint\">Click <strong>\u25b6 Run<\/strong> to see the output of all four mini-demos.<\/p>\n<ol>\n<li><strong>VARIANT<\/strong> stores different shapes of data in one column. <code>variant_type()<\/code> and <code>variant_keys()<\/code> let you inspect each value. Think of it as &#8220;JSON on steroids&#8221; \u2013 it is stored in a binary form, and common structure is split out automatically so queries are faster.<\/li>\n<li><strong>Lambda syntax<\/strong>: use <code>lambda x: x * 10<\/code> (Python style). The old arrow form <code>x -&gt; x * 10<\/code> now raises an error unless you turn it back on.<\/li>\n<li><strong><code>$min_qty<\/code><\/strong> is a short way to write <code>getvariable('min_qty')<\/code>.<\/li>\n<li><strong><code>10 \/\/ 0<\/code><\/strong> (integer division) and <code>% 0<\/code> now raise an error instead of quietly returning <code>NULL<\/code>. Wrap with <code>TRY()<\/code> if you want <code>NULL<\/code>. Note that <code>10 \/ 0<\/code> (normal division) still returns <code>inf<\/code>.<\/li>\n<\/ol>\n\n<h2 id=\"triggers\">Triggers: an automatic audit log<\/h2>\n<p>Triggers run SQL automatically when data changes. This is a classic use case: keep a history of price changes during a festival sale.<\/p>\n<div class=\"dd-vsc\" role=\"group\" aria-label=\"Code editor: audit.sql, run_sql.py\">\n<input type=\"radio\" name=\"v6\" id=\"v6f1\" class=\"f1\" checked>\n<input type=\"radio\" name=\"v6\" id=\"v6f2\" class=\"f2\">\n<input type=\"checkbox\" id=\"v6r2\" class=\"r2\">\n<div class=\"dd-vsc-bar\"><span class=\"dd-dots\"><i><\/i><i><\/i><i><\/i><\/span><span class=\"dd-vsc-title\">audit.sql \u2014 duckdb-demo \u2014 Visual Studio Code<\/span><\/div>\n<div class=\"dd-vsc-tabs\">\n<label for=\"v6f1\" class=\"t1\"><b class=\"ic sql\">SQL<\/b>audit.sql<\/label>\n<label for=\"v6f2\" class=\"t2\"><b class=\"ic py\">PY<\/b>run_sql.py<\/label>\n<label for=\"v6r2\" class=\"run run2\" title=\"Show the output in the terminal\">\u25b6 Run<\/label>\n<\/div>\n<div class=\"dd-vsc-pane p1\">\n<div class=\"dd-vsc-crumbs\">duckdb-demo \u203a audit.sql<\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"l\"><span class=\"c\">-- audit.sql: keep a history of price changes with a trigger<\/span><\/span>\n<span class=\"l\"><span class=\"k\">CREATE<\/span> <span class=\"k\">TABLE<\/span> <span class=\"n\">products<\/span> (<span class=\"n\">id<\/span> <span class=\"nb\">INTEGER<\/span> <span class=\"k\">PRIMARY<\/span> <span class=\"k\">KEY<\/span>, <span class=\"n\">name<\/span> <span class=\"nb\">VARCHAR<\/span>, <span class=\"n\">price<\/span> <span class=\"nb\">INTEGER<\/span>);<\/span>\n<span class=\"l\"><span class=\"k\">CREATE<\/span> <span class=\"k\">TABLE<\/span> <span class=\"n\">price_log<\/span> (<span class=\"n\">id<\/span> <span class=\"nb\">INTEGER<\/span>, <span class=\"n\">old_price<\/span> <span class=\"nb\">INTEGER<\/span>, <span class=\"n\">new_price<\/span> <span class=\"nb\">INTEGER<\/span>);<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"k\">CREATE<\/span> <span class=\"k\">TRIGGER<\/span> <span class=\"n\">log_price<\/span> <span class=\"k\">AFTER<\/span> <span class=\"k\">UPDATE<\/span> <span class=\"k\">ON<\/span> <span class=\"n\">products<\/span><\/span>\n<span class=\"l\"><span class=\"k\">REFERENCING<\/span> <span class=\"k\">OLD<\/span> <span class=\"k\">TABLE<\/span> <span class=\"k\">AS<\/span> <span class=\"n\">o<\/span> <span class=\"k\">NEW<\/span> <span class=\"k\">TABLE<\/span> <span class=\"k\">AS<\/span> <span class=\"n\">n<\/span><\/span>\n<span class=\"l\"><span class=\"k\">FOR<\/span> <span class=\"k\">EACH<\/span> <span class=\"k\">STATEMENT<\/span><\/span>\n<span class=\"l\"><span class=\"k\">INSERT<\/span> <span class=\"k\">INTO<\/span> <span class=\"n\">price_log<\/span><\/span>\n<span class=\"l\">    <span class=\"k\">SELECT<\/span> <span class=\"n\">n<\/span>.<span class=\"n\">id<\/span>, <span class=\"n\">o<\/span>.<span class=\"n\">price<\/span>, <span class=\"n\">n<\/span>.<span class=\"n\">price<\/span> <span class=\"k\">FROM<\/span> <span class=\"n\">o<\/span> <span class=\"k\">JOIN<\/span> <span class=\"n\">n<\/span> <span class=\"k\">ON<\/span> <span class=\"n\">o<\/span>.<span class=\"n\">id<\/span> = <span class=\"n\">n<\/span>.<span class=\"n\">id<\/span>;<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"k\">INSERT<\/span> <span class=\"k\">INTO<\/span> <span class=\"n\">products<\/span> <span class=\"k\">VALUES<\/span> (<span class=\"m\">1<\/span>, <span class=\"s\">'Laptop'<\/span>, <span class=\"m\">55000<\/span>), (<span class=\"m\">2<\/span>, <span class=\"s\">'Phone'<\/span>, <span class=\"m\">18000<\/span>);<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"c\">-- Festival sale: 10% off everything<\/span><\/span>\n<span class=\"l\"><span class=\"k\">UPDATE<\/span> <span class=\"n\">products<\/span> <span class=\"k\">SET<\/span> <span class=\"n\">price<\/span> = <span class=\"n\">price<\/span> * <span class=\"m\">0<\/span>.<span class=\"m\">9<\/span>;<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"k\">FROM<\/span> <span class=\"n\">price_log<\/span>;<\/span><\/code><\/pre>\n<div class=\"dd-vsc-status\"><span>\u2387 main<\/span><span>Ln 16, Col 1<\/span><span class=\"sp\"><\/span><span>Spaces: 4<\/span><span>UTF-8<\/span><span>LF<\/span><span>SQL<\/span><\/div>\n<\/div>\n<div class=\"dd-vsc-pane p2\">\n<div class=\"dd-vsc-crumbs\">duckdb-demo \u203a run_sql.py<\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"l\"><span class=\"kn\">import<\/span> <span class=\"n\">sys<\/span><\/span>\n<span class=\"l\"><span class=\"kn\">import<\/span> <span class=\"n\">duckdb<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"c\"># Run every statement in a .sql file and show the last result<\/span><\/span>\n<span class=\"l\"><span class=\"n\">script<\/span> = <span class=\"nb\">open<\/span>(<span class=\"n\">sys<\/span>.<span class=\"n\">argv<\/span>[<span class=\"m\">1<\/span>]).<span class=\"nf\">read<\/span>()<\/span>\n<span class=\"l\"><span class=\"n\">duckdb<\/span>.<span class=\"nf\">connect<\/span>().<span class=\"nf\">sql<\/span>(<span class=\"n\">script<\/span>).<span class=\"nf\">show<\/span>()<\/span><\/code><\/pre>\n<div class=\"dd-vsc-term\"><div class=\"dd-term-head\"><span>TERMINAL<\/span><span>OUTPUT<\/span><span>PROBLEMS<\/span><span class=\"dd-term-note\">Real output (DuckDB 2.0.0, Linux)<\/span><\/div><pre><code><span class=\"gp\">$<\/span> python3 run_sql.py audit.sql\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502  id   \u2502 old_price \u2502 new_price \u2502\n\u2502 int32 \u2502   int32   \u2502   int32   \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502     1 \u2502     55000 \u2502     49500 \u2502\n\u2502     2 \u2502     18000 \u2502     16200 \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518<\/code><\/pre><\/div>\n<div class=\"dd-vsc-status\"><span>\u2387 main<\/span><span>Ln 6, Col 1<\/span><span class=\"sp\"><\/span><span>Spaces: 4<\/span><span>UTF-8<\/span><span>LF<\/span><span>Python<\/span><\/div>\n<\/div>\n<\/div>\n<p class=\"dd-vsc-hint\">The first tab is the SQL file. Switch to <strong>run_sql.py<\/strong> and click <strong>\u25b6 Run<\/strong> to execute it.<\/p>\n<p><code>REFERENCING OLD TABLE AS o NEW TABLE AS n<\/code> gives you two temporary tables: the rows before and after the <code>UPDATE<\/code>. Because the trigger is <code>FOR EACH STATEMENT<\/code>, it runs once per <code>UPDATE<\/code>, not once per row \u2013 efficient for analytics. Some combinations are not supported yet, so check the <a href=\"https:\/\/duckdb.org\/docs\/2.0\/sql\/statements\/create_trigger.html\" rel=\"noopener\" target=\"_blank\">CREATE TRIGGER docs<\/a> before using them in production.<\/p>\n<p>The DuckDB command-line shell also got useful upgrades:<\/p>\n<div class=\"dd-vsc dd-vsc-termonly\" role=\"group\" aria-label=\"Terminal: Terminal \u2014 DuckDB CLI tips\">\n<div class=\"dd-vsc-bar\"><span class=\"dd-dots\"><i><\/i><i><\/i><i><\/i><\/span><span class=\"dd-vsc-title\">Terminal \u2014 DuckDB CLI tips<\/span><\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"l\"><span class=\"c\"># Open (or create) a database file in the CLI<\/span><\/span>\n<span class=\"l\"><span class=\"gp\">$<\/span> duckdb shop.duckdb<\/span>\n<span class=\"l\"><span class=\"c\"># Run one query and exit<\/span><\/span>\n<span class=\"l\"><span class=\"gp\">$<\/span> duckdb -c \"SELECT city, count(*) FROM 'sales.csv' GROUP BY city\"<\/span>\n<span class=\"l\"><span class=\"c\"># New in 2.0: agent mode (compact output for AI coding agents)<\/span><\/span>\n<span class=\"l\"><span class=\"gp\">$<\/span> duckdb -agent shop.duckdb<\/span>\n<span class=\"l\"><span class=\"c\"># Keep a new file readable by DuckDB 1.5<\/span><\/span>\n<span class=\"l\"><span class=\"gp\">$<\/span> duckdb old_friendly.duckdb -storage-version v1.5.6<\/span><\/code><\/pre>\n<\/div>\n<\/div>\n\n\n\n<div class=\"dd-post\">\n<h2 id=\"python-changes\">Python API changes in DuckDB 2.0<\/h2>\n<p>If you have DuckDB code from older tutorials (or from ChatGPT answers trained on them), some of it will break. Click each tab to see the change and the fix:<\/p>\n<div class=\"dd-tabs\">\n<input type=\"radio\" name=\"ddt\" id=\"ti1\" checked><input type=\"radio\" name=\"ddt\" id=\"ti2\"><input type=\"radio\" name=\"ddt\" id=\"ti3\"><input type=\"radio\" name=\"ddt\" id=\"ti4\">\n<div class=\"labels\"><label for=\"ti1\">Fetching rows<\/label><label for=\"ti2\">Open results<\/label><label for=\"ti3\">Threads<\/label><label for=\"ti4\">Files and versions<\/label><\/div>\n<div class=\"panel p1\"><p><strong>Before:<\/strong> <code>con.sql(q).fetchone()<\/code> and <code>fetchmany(n)<\/code> worked on a relation.<\/p><p><strong>Now:<\/strong> a relation is just a saved query \u2013 it runs again on every read, so <code>fetchone()<\/code>, <code>fetchmany()<\/code>, <code>execute()<\/code> and <code>fetch_df_chunk()<\/code> were removed from it. Use <code>con.execute(q).fetchone()<\/code>, or read a relation with <code>fetchall()<\/code>, <code>df()<\/code>, <code>pl()<\/code>, <code>iter_rows()<\/code> or <code>to_arrow_reader()<\/code>. Use <code>rel.materialize()<\/code> to compute the rows once and keep them.<\/p><\/div>\n<div class=\"panel p2\"><p><strong>Before:<\/strong> you could run new queries while an <code>execute()<\/code> result was only half read.<\/p><p><strong>Now:<\/strong> that raises <code>ResourceInUseException<\/code>. Read the result to the end, or call <code>con.complete()<\/code> (or <code>con.abort()<\/code>). A new <code>execute()<\/code> simply replaces the open result.<\/p><\/div>\n<div class=\"panel p3\"><p><strong>Before:<\/strong> threads sharing one connection waited for each other.<\/p><p><strong>Now:<\/strong> using one connection from two threads at the same time raises <code>ResourceInUseException<\/code>. Give each thread its own <code>con.cursor()<\/code>. In a web API (for example <a href=\"https:\/\/devdojo.co.in\/index.php\/2026\/10\/15\/fastapi-tutorial\/\">FastAPI<\/a>), create a cursor per request.<\/p><\/div>\n<div class=\"panel p4\"><p><strong>Python version:<\/strong> DuckDB 2.0 needs Python 3.11 or newer (free-threaded builds are not supported).<\/p><p><strong>Database files:<\/strong> DuckDB 2.0 can open your old files, but DuckDB 1.x cannot open files that 2.0 creates. To stay compatible, connect with <code>config={\"storage_compatibility_version\": \"v1.5.0\"}<\/code>.<\/p><p><strong>Also changed:<\/strong> <code>con.sql(\"CREATE ...\")<\/code> now returns a relation instead of <code>None<\/code>, a failed statement inside a transaction aborts the whole transaction, and <code>write_csv(..., overwrite=False)<\/code> now raises an error if the file exists.<\/p><\/div>\n<\/div>\n<p>Here is the same small script written the old way and the 2.0 way:<\/p>\n<div class=\"dd-vsc\" role=\"group\" aria-label=\"Code editor: old_1x.py, new_2x.py\">\n<input type=\"radio\" name=\"v7\" id=\"v7f1\" class=\"f1\" checked>\n<input type=\"radio\" name=\"v7\" id=\"v7f2\" class=\"f2\">\n<input type=\"checkbox\" id=\"v7r1\" class=\"r1\">\n<input type=\"checkbox\" id=\"v7r2\" class=\"r2\">\n<div class=\"dd-vsc-bar\"><span class=\"dd-dots\"><i><\/i><i><\/i><i><\/i><\/span><span class=\"dd-vsc-title\">old_1x.py \u2014 duckdb-upgrade \u2014 Visual Studio Code<\/span><\/div>\n<div class=\"dd-vsc-tabs\">\n<label for=\"v7f1\" class=\"t1\"><b class=\"ic py\">PY<\/b>old_1x.py<\/label>\n<label for=\"v7f2\" class=\"t2\"><b class=\"ic py\">PY<\/b>new_2x.py<\/label>\n<label for=\"v7r1\" class=\"run run1\" title=\"Show the output in the terminal\">\u25b6 Run<\/label>\n<label for=\"v7r2\" class=\"run run2\" title=\"Show the output in the terminal\">\u25b6 Run<\/label>\n<\/div>\n<div class=\"dd-vsc-pane p1\">\n<div class=\"dd-vsc-crumbs\">duckdb-upgrade \u203a old_1x.py<\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"l\"><span class=\"c\"># Works on DuckDB 1.x - FAILS on DuckDB 2.0<\/span><\/span>\n<span class=\"l\"><span class=\"kn\">import<\/span> <span class=\"n\">duckdb<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">con<\/span> = <span class=\"n\">duckdb<\/span>.<span class=\"nf\">connect<\/span>()<\/span>\n<span class=\"l\"><span class=\"n\">rel<\/span> = <span class=\"n\">con<\/span>.<span class=\"nf\">sql<\/span>(<span class=\"s\">\"SELECT 42 AS answer\"<\/span>)<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"nb\">print<\/span>(<span class=\"n\">rel<\/span>.<span class=\"nf\">fetchone<\/span>())                <span class=\"c\"># AttributeError on 2.0<\/span><\/span>\n<span class=\"l\"><span class=\"nb\">print<\/span>(<span class=\"n\">con<\/span>.<span class=\"nf\">sql<\/span>(<span class=\"s\">\"SELECT list_transform([1, 2], x -&gt; x + 1)\"<\/span>).<span class=\"nf\">fetchall<\/span>())<\/span><\/code><\/pre>\n<div class=\"dd-vsc-term\"><div class=\"dd-term-head\"><span>TERMINAL<\/span><span>OUTPUT<\/span><span>PROBLEMS<\/span><span class=\"dd-term-note\">Real error on DuckDB 2.0.0 (traceback shortened)<\/span><\/div><pre><code><span class=\"gp\">$<\/span> python3 old_1x.py\nTraceback (most recent call last):\n  ...\nAttributeError: Relations no longer have fetchone(): a relation keeps\nno result between calls, so every call runs it again.\nUse rel.limit(1).fetchall() instead.<\/code><\/pre><\/div>\n<div class=\"dd-vsc-status\"><span>\u2387 main<\/span><span>Ln 8, Col 1<\/span><span class=\"sp\"><\/span><span>Spaces: 4<\/span><span>UTF-8<\/span><span>LF<\/span><span>Python<\/span><\/div>\n<\/div>\n<div class=\"dd-vsc-pane p2\">\n<div class=\"dd-vsc-crumbs\">duckdb-upgrade \u203a new_2x.py<\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"l\"><span class=\"c\"># The DuckDB 2.0 way<\/span><\/span>\n<span class=\"l\"><span class=\"kn\">import<\/span> <span class=\"n\">duckdb<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">con<\/span> = <span class=\"n\">duckdb<\/span>.<span class=\"nf\">connect<\/span>()<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"c\"># fetchone(): use execute() - it returns a result you can read row by row<\/span><\/span>\n<span class=\"l\"><span class=\"nb\">print<\/span>(<span class=\"n\">con<\/span>.<span class=\"nf\">execute<\/span>(<span class=\"s\">\"SELECT 42 AS answer\"<\/span>).<span class=\"nf\">fetchone<\/span>())<\/span>\n<span class=\"l\"><span class=\"n\">con<\/span>.<span class=\"nf\">complete<\/span>()   <span class=\"c\"># finish the open result before running the next query<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"c\"># Relations (con.sql): read them with fetchall(), df(), pl() or iter_rows()<\/span><\/span>\n<span class=\"l\"><span class=\"nb\">print<\/span>(<span class=\"n\">con<\/span>.<span class=\"nf\">sql<\/span>(<span class=\"s\">\"SELECT list_transform([1, 2], lambda x: x + 1)\"<\/span>).<span class=\"nf\">fetchall<\/span>())<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"c\"># Threads: give every thread its own cursor<\/span><\/span>\n<span class=\"l\"><span class=\"n\">cur<\/span> = <span class=\"n\">con<\/span>.<span class=\"nf\">cursor<\/span>()<\/span>\n<span class=\"l\"><span class=\"nb\">print<\/span>(<span class=\"n\">cur<\/span>.<span class=\"nf\">execute<\/span>(<span class=\"s\">\"SELECT 'one cursor per thread'\"<\/span>).<span class=\"nf\">fetchall<\/span>())<\/span><\/code><\/pre>\n<div class=\"dd-vsc-term\"><div class=\"dd-term-head\"><span>TERMINAL<\/span><span>OUTPUT<\/span><span>PROBLEMS<\/span><span class=\"dd-term-note\">Real output (DuckDB 2.0.0, Linux)<\/span><\/div><pre><code><span class=\"gp\">$<\/span> python3 new_2x.py\n(42,)\n[([2, 3],)]\n[('one cursor per thread',)]<\/code><\/pre><\/div>\n<div class=\"dd-vsc-status\"><span>\u2387 main<\/span><span>Ln 15, Col 1<\/span><span class=\"sp\"><\/span><span>Spaces: 4<\/span><span>UTF-8<\/span><span>LF<\/span><span>Python<\/span><\/div>\n<\/div>\n<\/div>\n<p class=\"dd-vsc-hint\">Run <strong>old_1x.py<\/strong> to see the real error, then switch to <strong>new_2x.py<\/strong> and run the fixed version.<\/p>\n\n<h2 id=\"upgrade\">Upgrade from DuckDB 1.x: checklist<\/h2>\n<ol class=\"dd-steps\">\n<li><strong>Test on a copy first.<\/strong> The DuckDB team advises a shadow system, and for mission-critical work, waiting for the first patch release planned for November.<\/li>\n<li><strong>Back up your <code>.duckdb<\/code> files<\/strong>, or keep them in the old format with <code>storage_compatibility_version<\/code> if other tools still use DuckDB 1.x.<\/li>\n<li><strong>Search your SQL for <code>-&gt;<\/code> lambdas<\/strong> and change them to <code>lambda x: ...<\/code>. (Temporary escape hatch: <code>SET lambda_syntax = 'enable_single_arrow';<\/code> \u2013 it goes away in 2.1.)<\/li>\n<li><strong>Check regex and maths:<\/strong> <code>~<\/code> now does partial matching (<code>'abc' ~ 'b'<\/code> is true), <code>2 * 3!<\/code> is now 12, and <code>\/\/<\/code> or <code>%<\/code> by zero raise errors.<\/li>\n<li><strong>Check <code>MERGE INTO<\/code><\/strong>: if several source rows match one target row, it now errors instead of picking one.<\/li>\n<li><strong>Update Python code<\/strong> as shown above, and read the official <a href=\"https:\/\/duckdb.org\/docs\/2.0\/operations_manual\/migration.html\" rel=\"noopener\" target=\"_blank\">DuckDB 2.0 migration guide<\/a>.<\/li>\n<\/ol>\n\n<h2 id=\"compare\">DuckDB vs SQLite vs PostgreSQL<\/h2>\n<div class=\"dd-tablewrap\"><table>\n<thead><tr><th><\/th><th>DuckDB<\/th><th>SQLite<\/th><th>PostgreSQL<\/th><\/tr><\/thead>\n<tbody>\n<tr><td>Runs as<\/td><td>Library inside your app<\/td><td>Library inside your app<\/td><td>Separate server<\/td><\/tr>\n<tr><td>Best for<\/td><td>Analytics, reports, data files<\/td><td>Small app storage, mobile<\/td><td>Web apps with many users<\/td><\/tr>\n<tr><td>Storage<\/td><td>Column by column<\/td><td>Row by row<\/td><td>Row by row<\/td><\/tr>\n<tr><td>Many writers at once<\/td><td>No (one writer process)<\/td><td>Limited<\/td><td>Yes<\/td><\/tr>\n<tr><td>Query CSV\/Parquet directly<\/td><td>Yes, built in<\/td><td>No<\/td><td>Needs extensions<\/td><\/tr>\n<\/tbody><\/table><\/div>\n<p>They work great together: PostgreSQL for your app (see our <a href=\"https:\/\/devdojo.co.in\/index.php\/2026\/10\/09\/postgresql-19-new-features\/\">PostgreSQL 19 guide<\/a>), DuckDB for reports on top of exported data. DuckDB can even attach a PostgreSQL database and query it directly with its <code>postgres<\/code> extension.<\/p>\n<\/div>\n\n\n\n<div class=\"dd-post\">\n<h2 id=\"errors\">Common errors and fixes<\/h2>\n<div class=\"dd-tablewrap\"><table>\n<thead><tr><th>Error message (shortened)<\/th><th>Why it happens<\/th><th>Fix<\/th><\/tr><\/thead>\n<tbody>\n<tr><td><code>Relations no longer have fetchone()<\/code><\/td><td>2.0 removed row-by-row reading from <code>con.sql()<\/code> relations.<\/td><td>Use <code>con.execute(q).fetchone()<\/code> or <code>rel.fetchall()<\/code>.<\/td><\/tr>\n<tr><td><code>ResourceInUseException: connection has an unfinished result from execute()<\/code><\/td><td>You started a new query before finishing the previous <code>execute()<\/code> result.<\/td><td>Fetch all rows, or call <code>con.complete()<\/code>; use <code>con.cursor()<\/code> per thread.<\/td><\/tr>\n<tr><td><code>Binder Error: Deprecated lambda arrow (-&gt;) detected<\/code><\/td><td>Old <code>x -&gt; x + 1<\/code> lambda syntax.<\/td><td>Write <code>lambda x: x + 1<\/code>.<\/td><\/tr>\n<tr><td><code>Invalid Input Error: Division by zero in expression (10 \/\/ 0)<\/code><\/td><td>Integer division by zero now raises an error.<\/td><td><code>TRY(10 \/\/ 0)<\/code> or <code>NULLIF(divisor, 0)<\/code>.<\/td><\/tr>\n<tr><td><code>IO Error: No files found that match the pattern \"missing.csv\"<\/code><\/td><td>Wrong path \u2013 paths are relative to where you run the script.<\/td><td>Use an absolute path or <code>cd<\/code> into the data folder.<\/td><\/tr>\n<tr><td>Old tool cannot open a new <code>.duckdb<\/code> file<\/td><td>Files created by 2.0 use a new storage format.<\/td><td>Upgrade the tool, or create files with <code>storage_compatibility_version<\/code>.<\/td><\/tr>\n<tr><td>&#8220;Could not set lock on file&#8221;<\/td><td>Two processes open the same file for writing.<\/td><td>Use one writer process, or open others with <code>read_only=True<\/code>.<\/td><\/tr>\n<\/tbody><\/table><\/div>\n\n<h2 id=\"best-practices\">Best practices<\/h2>\n<ul>\n<li><strong>Convert big CSVs to Parquet once<\/strong>, then query the Parquet. It is smaller and much faster to read.<\/li>\n<li><strong>Select only the columns you need<\/strong> \u2013 column storage rewards you for it.<\/li>\n<li><strong>Use <code>execute()<\/code> for writes and parameters<\/strong>: <code>con.execute(\"SELECT * FROM sales WHERE city = ?\", [\"Pune\"])<\/code>. Never build SQL by joining user input strings.<\/li>\n<li><strong>One connection, many cursors<\/strong> in multi-threaded apps and web servers.<\/li>\n<li><strong>Pin the version<\/strong> in <code>requirements.txt<\/code> \/ <code>package.json<\/code> (for example <code>duckdb==2.0.0<\/code>) so upgrades are deliberate.<\/li>\n<li><strong>Do not use DuckDB as your main web app database<\/strong> with many concurrent writers \u2013 use PostgreSQL there and DuckDB for analytics. To run both side by side locally, see our <a href=\"https:\/\/devdojo.co.in\/index.php\/2026\/10\/10\/docker-compose-tutorial\/\">Docker Compose tutorial<\/a>.<\/li>\n<\/ul>\n\n<h2 id=\"practice\">Practice task<\/h2>\n<p>Use the same <code>sales.csv<\/code> and complete the TODOs. It takes about 15 minutes and uses everything from this post.<\/p>\n<div class=\"dd-vsc\" role=\"group\" aria-label=\"Code editor: practice.py\">\n<input type=\"radio\" name=\"v8\" id=\"v8f1\" class=\"f1\" checked>\n<div class=\"dd-vsc-bar\"><span class=\"dd-dots\"><i><\/i><i><\/i><i><\/i><\/span><span class=\"dd-vsc-title\">practice.py \u2014 duckdb-practice \u2014 Visual Studio Code<\/span><\/div>\n<div class=\"dd-vsc-tabs\">\n<label for=\"v8f1\" class=\"t1\"><b class=\"ic py\">PY<\/b>practice.py<\/label>\n<\/div>\n<div class=\"dd-vsc-pane p1\">\n<div class=\"dd-vsc-crumbs\">duckdb-practice \u203a practice.py<\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"l\"><span class=\"kn\">import<\/span> <span class=\"n\">duckdb<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">con<\/span> = <span class=\"n\">duckdb<\/span>.<span class=\"nf\">connect<\/span>(<span class=\"s\">\"practice.duckdb\"<\/span>)<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"c\"># TODO 1: create a table \"sales\" from sales.csv<\/span><\/span>\n<span class=\"l\"><span class=\"c\"># TODO 2: find the best-selling PRODUCT in each CITY (by revenue)<\/span><\/span>\n<span class=\"l\"><span class=\"c\">#         hint: use a window function -&gt; row_number() OVER (PARTITION BY city ...)<\/span><\/span>\n<span class=\"l\"><span class=\"c\"># TODO 3: save the answer to best_products.parquet<\/span><\/span>\n<span class=\"l\"><span class=\"c\"># TODO 4: read best_products.parquet back and print it with .show()<\/span><\/span><\/code><\/pre>\n<div class=\"dd-vsc-status\"><span>\u2387 main<\/span><span>Ln 9, Col 1<\/span><span class=\"sp\"><\/span><span>Spaces: 4<\/span><span>UTF-8<\/span><span>LF<\/span><span>Python<\/span><\/div>\n<\/div>\n<\/div>\n<p class=\"dd-vsc-hint\">Stuck on TODO 2? Look up <code>QUALIFY<\/code> in the DuckDB docs \u2013 it filters window function results without a subquery.<\/p>\n\n<h2 id=\"faq\">FAQ<\/h2>\n<details><summary>Is DuckDB free for commercial use?<\/summary><p>Yes. DuckDB is open source under the MIT license, so you can use it in personal and commercial projects.<\/p><\/details>\n<details><summary>Is DuckDB a replacement for PostgreSQL or MySQL?<\/summary><p>Not for typical web apps. DuckDB is built for analytics with one writer process. Use PostgreSQL or MySQL for app data with many users, and DuckDB for reports, data files and pipelines.<\/p><\/details>\n<details><summary>Can DuckDB handle data bigger than my RAM?<\/summary><p>Yes, in many cases. DuckDB streams data from files and can spill to disk for big joins and aggregations, so it can process data sets larger than memory \u2013 just slower than fully in-memory work.<\/p><\/details>\n<details><summary>Should I upgrade to DuckDB 2.0 now?<\/summary><p>For new projects, yes \u2013 it is a long-term support release. For important existing systems, test on a copy first and read the migration guide; the DuckDB team suggests waiting for the November patch release for mission-critical workloads.<\/p><\/details>\n<details><summary>Does DuckDB work in Node.js and the browser?<\/summary><p>Yes. Use <code>@duckdb\/node-api<\/code> in Node.js. There is also DuckDB-Wasm for running DuckDB inside a web page. New to Node.js? Start with our <a href=\"https:\/\/devdojo.co.in\/index.php\/2026\/10\/23\/nodejs-series-part-1-what-is-nodejs\/\">Node.js Part 1 guide<\/a>.<\/p><\/details>\n\n<h2>Conclusion<\/h2>\n<p>DuckDB turns your laptop into a fast analytics machine: install one package, point SQL at a CSV or Parquet file, and get answers in milliseconds. Version 2.0 makes it a stable, long-term choice and adds <code>VARIANT<\/code>, triggers and a friendlier SQL dialect \u2013 just remember the Python API changes when you upgrade old code. Start with the practice task, then try DuckDB on a real data file from your job or college project.<\/p>\n\n<div class=\"dd-cta\"><h3>New posts every day on DevDojo<\/h3><p>Practical tutorials on AI, frontend, backend, databases and DevOps \u2013 with runnable code. Bookmark <a href=\"https:\/\/devdojo.co.in\/\">devdojo.co.in<\/a> and share this DuckDB tutorial with a friend who still waits for big CSV files to load in Excel.<\/p><\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>DuckDB tutorial for beginners: install DuckDB 2.0, query CSV, Parquet and JSON with SQL in Python and Node.js, and learn what changed in the 2.0 release.<\/p>\n","protected":false},"author":1,"featured_media":237,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[69,9],"tags":[129,128,43,130,52,72],"class_list":["post-238","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-databases","category-python","tag-data-analysis","tag-duckdb","tag-node-js","tag-parquet","tag-python","tag-sql"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.4 - aioseo.com -->\n\t<meta name=\"description\" content=\"DuckDB tutorial for beginners: install DuckDB 2.0, query CSV, Parquet and JSON with SQL in Python and Node.js, and learn what changed in the 2.0 release.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Lokendra\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/devdojo.co.in\/index.php\/2026\/10\/25\/duckdb-2-tutorial-python-nodejs\/\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.4\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_US\" \/>\n\t\t<meta property=\"og:site_name\" content=\"DevDojo - Empowering Developers\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"DuckDB 2.0 Tutorial: Fast SQL on CSV, Parquet &amp; JSON\" \/>\n\t\t<meta property=\"og:description\" content=\"DuckDB tutorial for beginners: install DuckDB 2.0, query CSV, Parquet and JSON with SQL in Python and Node.js, and learn what changed in the 2.0 release.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/devdojo.co.in\/index.php\/2026\/10\/25\/duckdb-2-tutorial-python-nodejs\/\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2026-10-25T17:10:39+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2026-10-25T17:10:40+00:00\" \/>\n\t\t<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n\t\t<meta name=\"twitter:title\" content=\"DuckDB 2.0 Tutorial: Fast SQL on CSV, Parquet &amp; JSON\" \/>\n\t\t<meta name=\"twitter:description\" content=\"DuckDB tutorial for beginners: install DuckDB 2.0, query CSV, Parquet and JSON with SQL in Python and Node.js, and learn what changed in the 2.0 release.\" \/>\n\t\t<script type=\"application\/ld+json\" class=\"aioseo-schema\">\n\t\t\t{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"BlogPosting\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/25\\\/duckdb-2-tutorial-python-nodejs\\\/#blogposting\",\"name\":\"DuckDB 2.0 Tutorial: Fast SQL on CSV, Parquet & JSON\",\"headline\":\"DuckDB 2.0 Tutorial: Fast SQL on CSV, Parquet and JSON in Python and Node.js\",\"author\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/author\\\/lokendra\\\/#author\"},\"publisher\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/#organization\"},\"image\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/devdojo.co.in\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/duckdb-2-tutorial-python-nodejs.jpg\",\"width\":1200,\"height\":630,\"caption\":\"DuckDB 2.0 tutorial banner: CSV, Parquet and JSON files flowing into an in-process SQL engine\"},\"datePublished\":\"2026-10-25T17:10:39+00:00\",\"dateModified\":\"2026-10-25T17:10:40+00:00\",\"inLanguage\":\"en-US\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/25\\\/duckdb-2-tutorial-python-nodejs\\\/#webpage\"},\"isPartOf\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/25\\\/duckdb-2-tutorial-python-nodejs\\\/#webpage\"},\"articleSection\":\"Databases, Python, Data Analysis, DuckDB, Node.js, Parquet, Python, SQL\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/25\\\/duckdb-2-tutorial-python-nodejs\\\/#breadcrumblist\",\"itemListElement\":[{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/#listItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/devdojo.co.in\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/category\\\/backend\\\/#listItem\",\"name\":\"Backend\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/category\\\/backend\\\/#listItem\",\"position\":2,\"name\":\"Backend\",\"item\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/category\\\/backend\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/category\\\/backend\\\/python\\\/#listItem\",\"name\":\"Python\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/#listItem\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/category\\\/backend\\\/python\\\/#listItem\",\"position\":3,\"name\":\"Python\",\"item\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/category\\\/backend\\\/python\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/25\\\/duckdb-2-tutorial-python-nodejs\\\/#listItem\",\"name\":\"DuckDB 2.0 Tutorial: Fast SQL on CSV, Parquet and JSON in Python and Node.js\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/category\\\/backend\\\/#listItem\",\"name\":\"Backend\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/25\\\/duckdb-2-tutorial-python-nodejs\\\/#listItem\",\"position\":4,\"name\":\"DuckDB 2.0 Tutorial: Fast SQL on CSV, Parquet and JSON in Python and Node.js\",\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/category\\\/backend\\\/python\\\/#listItem\",\"name\":\"Python\"}}]},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/#organization\",\"name\":\"DevDojo\",\"description\":\"Empowering Developers\",\"url\":\"https:\\\/\\\/devdojo.co.in\\\/\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/author\\\/lokendra\\\/#author\",\"url\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/author\\\/lokendra\\\/\",\"name\":\"Lokendra\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/25\\\/duckdb-2-tutorial-python-nodejs\\\/#authorImage\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/99f102f0cc9080237ce54b91304fd1b3b6fa29925cbaeba93cd796872e58b45c?s=96&d=mm&r=g\",\"width\":96,\"height\":96,\"caption\":\"Lokendra\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/25\\\/duckdb-2-tutorial-python-nodejs\\\/#webpage\",\"url\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/25\\\/duckdb-2-tutorial-python-nodejs\\\/\",\"name\":\"DuckDB 2.0 Tutorial: Fast SQL on CSV, Parquet & JSON\",\"description\":\"DuckDB tutorial for beginners: install DuckDB 2.0, query CSV, Parquet and JSON with SQL in Python and Node.js, and learn what changed in the 2.0 release.\",\"inLanguage\":\"en-US\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/25\\\/duckdb-2-tutorial-python-nodejs\\\/#breadcrumblist\"},\"author\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/author\\\/lokendra\\\/#author\"},\"creator\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/author\\\/lokendra\\\/#author\"},\"image\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/devdojo.co.in\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/duckdb-2-tutorial-python-nodejs.jpg\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/25\\\/duckdb-2-tutorial-python-nodejs\\\/#mainImage\",\"width\":1200,\"height\":630,\"caption\":\"DuckDB 2.0 tutorial banner: CSV, Parquet and JSON files flowing into an in-process SQL engine\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/25\\\/duckdb-2-tutorial-python-nodejs\\\/#mainImage\"},\"datePublished\":\"2026-10-25T17:10:39+00:00\",\"dateModified\":\"2026-10-25T17:10:40+00:00\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/#website\",\"url\":\"https:\\\/\\\/devdojo.co.in\\\/\",\"name\":\"DevDojo\",\"description\":\"Empowering Developers\",\"inLanguage\":\"en-US\",\"publisher\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/#organization\"}}]}\n\t\t<\/script>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"DuckDB 2.0 Tutorial: Fast SQL on CSV, Parquet & JSON","description":"DuckDB tutorial for beginners: install DuckDB 2.0, query CSV, Parquet and JSON with SQL in Python and Node.js, and learn what changed in the 2.0 release.","canonical_url":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/25\/duckdb-2-tutorial-python-nodejs\/","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"BlogPosting","@id":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/25\/duckdb-2-tutorial-python-nodejs\/#blogposting","name":"DuckDB 2.0 Tutorial: Fast SQL on CSV, Parquet & JSON","headline":"DuckDB 2.0 Tutorial: Fast SQL on CSV, Parquet and JSON in Python and Node.js","author":{"@id":"https:\/\/devdojo.co.in\/index.php\/author\/lokendra\/#author"},"publisher":{"@id":"https:\/\/devdojo.co.in\/#organization"},"image":{"@type":"ImageObject","url":"https:\/\/devdojo.co.in\/wp-content\/uploads\/2026\/10\/duckdb-2-tutorial-python-nodejs.jpg","width":1200,"height":630,"caption":"DuckDB 2.0 tutorial banner: CSV, Parquet and JSON files flowing into an in-process SQL engine"},"datePublished":"2026-10-25T17:10:39+00:00","dateModified":"2026-10-25T17:10:40+00:00","inLanguage":"en-US","mainEntityOfPage":{"@id":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/25\/duckdb-2-tutorial-python-nodejs\/#webpage"},"isPartOf":{"@id":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/25\/duckdb-2-tutorial-python-nodejs\/#webpage"},"articleSection":"Databases, Python, Data Analysis, DuckDB, Node.js, Parquet, Python, SQL"},{"@type":"BreadcrumbList","@id":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/25\/duckdb-2-tutorial-python-nodejs\/#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/devdojo.co.in\/#listItem","position":1,"name":"Home","item":"https:\/\/devdojo.co.in\/","nextItem":{"@type":"ListItem","@id":"https:\/\/devdojo.co.in\/index.php\/category\/backend\/#listItem","name":"Backend"}},{"@type":"ListItem","@id":"https:\/\/devdojo.co.in\/index.php\/category\/backend\/#listItem","position":2,"name":"Backend","item":"https:\/\/devdojo.co.in\/index.php\/category\/backend\/","nextItem":{"@type":"ListItem","@id":"https:\/\/devdojo.co.in\/index.php\/category\/backend\/python\/#listItem","name":"Python"},"previousItem":{"@type":"ListItem","@id":"https:\/\/devdojo.co.in\/#listItem","name":"Home"}},{"@type":"ListItem","@id":"https:\/\/devdojo.co.in\/index.php\/category\/backend\/python\/#listItem","position":3,"name":"Python","item":"https:\/\/devdojo.co.in\/index.php\/category\/backend\/python\/","nextItem":{"@type":"ListItem","@id":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/25\/duckdb-2-tutorial-python-nodejs\/#listItem","name":"DuckDB 2.0 Tutorial: Fast SQL on CSV, Parquet and JSON in Python and Node.js"},"previousItem":{"@type":"ListItem","@id":"https:\/\/devdojo.co.in\/index.php\/category\/backend\/#listItem","name":"Backend"}},{"@type":"ListItem","@id":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/25\/duckdb-2-tutorial-python-nodejs\/#listItem","position":4,"name":"DuckDB 2.0 Tutorial: Fast SQL on CSV, Parquet and JSON in Python and Node.js","previousItem":{"@type":"ListItem","@id":"https:\/\/devdojo.co.in\/index.php\/category\/backend\/python\/#listItem","name":"Python"}}]},{"@type":"Organization","@id":"https:\/\/devdojo.co.in\/#organization","name":"DevDojo","description":"Empowering Developers","url":"https:\/\/devdojo.co.in\/"},{"@type":"Person","@id":"https:\/\/devdojo.co.in\/index.php\/author\/lokendra\/#author","url":"https:\/\/devdojo.co.in\/index.php\/author\/lokendra\/","name":"Lokendra","image":{"@type":"ImageObject","@id":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/25\/duckdb-2-tutorial-python-nodejs\/#authorImage","url":"https:\/\/secure.gravatar.com\/avatar\/99f102f0cc9080237ce54b91304fd1b3b6fa29925cbaeba93cd796872e58b45c?s=96&d=mm&r=g","width":96,"height":96,"caption":"Lokendra"}},{"@type":"WebPage","@id":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/25\/duckdb-2-tutorial-python-nodejs\/#webpage","url":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/25\/duckdb-2-tutorial-python-nodejs\/","name":"DuckDB 2.0 Tutorial: Fast SQL on CSV, Parquet & JSON","description":"DuckDB tutorial for beginners: install DuckDB 2.0, query CSV, Parquet and JSON with SQL in Python and Node.js, and learn what changed in the 2.0 release.","inLanguage":"en-US","isPartOf":{"@id":"https:\/\/devdojo.co.in\/#website"},"breadcrumb":{"@id":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/25\/duckdb-2-tutorial-python-nodejs\/#breadcrumblist"},"author":{"@id":"https:\/\/devdojo.co.in\/index.php\/author\/lokendra\/#author"},"creator":{"@id":"https:\/\/devdojo.co.in\/index.php\/author\/lokendra\/#author"},"image":{"@type":"ImageObject","url":"https:\/\/devdojo.co.in\/wp-content\/uploads\/2026\/10\/duckdb-2-tutorial-python-nodejs.jpg","@id":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/25\/duckdb-2-tutorial-python-nodejs\/#mainImage","width":1200,"height":630,"caption":"DuckDB 2.0 tutorial banner: CSV, Parquet and JSON files flowing into an in-process SQL engine"},"primaryImageOfPage":{"@id":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/25\/duckdb-2-tutorial-python-nodejs\/#mainImage"},"datePublished":"2026-10-25T17:10:39+00:00","dateModified":"2026-10-25T17:10:40+00:00"},{"@type":"WebSite","@id":"https:\/\/devdojo.co.in\/#website","url":"https:\/\/devdojo.co.in\/","name":"DevDojo","description":"Empowering Developers","inLanguage":"en-US","publisher":{"@id":"https:\/\/devdojo.co.in\/#organization"}}]},"og:locale":"en_US","og:site_name":"DevDojo - Empowering Developers","og:type":"article","og:title":"DuckDB 2.0 Tutorial: Fast SQL on CSV, Parquet &amp; JSON","og:description":"DuckDB tutorial for beginners: install DuckDB 2.0, query CSV, Parquet and JSON with SQL in Python and Node.js, and learn what changed in the 2.0 release.","og:url":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/25\/duckdb-2-tutorial-python-nodejs\/","article:published_time":"2026-10-25T17:10:39+00:00","article:modified_time":"2026-10-25T17:10:40+00:00","twitter:card":"summary_large_image","twitter:title":"DuckDB 2.0 Tutorial: Fast SQL on CSV, Parquet &amp; JSON","twitter:description":"DuckDB tutorial for beginners: install DuckDB 2.0, query CSV, Parquet and JSON with SQL in Python and Node.js, and learn what changed in the 2.0 release."},"aioseo_meta_data":{"post_id":"238","title":"DuckDB 2.0 Tutorial: Fast SQL on CSV, Parquet & JSON","description":"DuckDB tutorial for beginners: install DuckDB 2.0, query CSV, Parquet and JSON with SQL in Python and Node.js, and learn what changed in the 2.0 release.","keywords":null,"keyphrases":{"focus":{"keyphrase":"DuckDB tutorial","score":72},"additional":[]},"primary_term":null,"canonical_url":null,"og_title":"DuckDB 2.0 Tutorial: Fast SQL on CSV, Parquet & JSON","og_description":"DuckDB tutorial for beginners: install DuckDB 2.0, query CSV, Parquet and JSON with SQL in Python and Node.js, and learn what changed in the 2.0 release.","og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":"","og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"BlogPosting","isEnabled":true},"graphs":[]},"schema_type":"default","schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":"-1","robots_max_videopreview":"-1","robots_max_imagepreview":"large","priority":0,"frequency":"default","local_seo":null,"breadcrumb_settings":null,"limit_modified_date":false,"ai":{"faqs":[],"keyPoints":[],"schemas":[],"titles":[],"descriptions":[],"socialPosts":{"email":{"subject":"","preview":"","content":""},"linkedin":[],"twitter":[],"facebook":[],"instagram":[]}},"created":"2026-10-24 14:08:32","updated":"2026-10-25 17:35:55","focus_keyword":"DuckDB tutorial","additional_keywords":null,"truseo_locale":null,"seo_analyzer_scan_date":null},"aioseo_breadcrumb":"<div class=\"aioseo-breadcrumbs\"><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/devdojo.co.in\/\" title=\"Home\">Home<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">\u00bb<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/devdojo.co.in\/index.php\/category\/backend\/\" title=\"Backend\">Backend<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">\u00bb<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/devdojo.co.in\/index.php\/category\/backend\/python\/\" title=\"Python\">Python<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">\u00bb<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\tDuckDB 2.0 Tutorial: Fast SQL on CSV, Parquet and JSON in Python and Node.js\n\t\t<\/span><\/div>","aioseo_breadcrumb_json":[{"label":"Home","link":"https:\/\/devdojo.co.in\/"},{"label":"Backend","link":"https:\/\/devdojo.co.in\/index.php\/category\/backend\/"},{"label":"Python","link":"https:\/\/devdojo.co.in\/index.php\/category\/backend\/python\/"},{"label":"DuckDB 2.0 Tutorial: Fast SQL on CSV, Parquet and JSON in Python and Node.js","link":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/25\/duckdb-2-tutorial-python-nodejs\/"}],"_links":{"self":[{"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/posts\/238","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/comments?post=238"}],"version-history":[{"count":1,"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/posts\/238\/revisions"}],"predecessor-version":[{"id":241,"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/posts\/238\/revisions\/241"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/media\/237"}],"wp:attachment":[{"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/media?parent=238"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/categories?post=238"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/tags?post=238"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}