[{"data":1,"prerenderedAt":3075},["ShallowReactive",2],{"navigation_docs":3,"-tu-hoc-database-postgresql-cu-phap-co-ban":420,"-tu-hoc-database-postgresql-cu-phap-co-ban-surround":3070},[4,153],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":26},"Phỏng vấn .NET","i-lucide-briefcase","\u002Fphong-van","1.phong-van",[10,27,66,111,128],{"title":11,"icon":12,"path":13,"stem":14,"children":15,"page":26},"Tổng quan","i-lucide-compass","\u002Fphong-van\u002Ftong-quan","1.phong-van\u002F1.tong-quan",[16,21],{"title":17,"path":18,"stem":19,"icon":20},"Thị trường HCM","\u002Fphong-van\u002Ftong-quan\u002Fthi-truong","1.phong-van\u002F1.tong-quan\u002F1.thi-truong","i-lucide-trending-up",{"title":22,"path":23,"stem":24,"icon":25},"Lộ trình 7 ngày","\u002Fphong-van\u002Ftong-quan\u002Flo-trinh","1.phong-van\u002F1.tong-quan\u002F2.lo-trinh","i-lucide-calendar-days",false,{"title":22,"icon":25,"path":28,"stem":29,"children":30,"page":26},"\u002Fphong-van\u002Flo-trinh","1.phong-van\u002F2.lo-trinh",[31,36,41,46,51,56,61],{"title":32,"path":33,"stem":34,"icon":35},"Ngày 1 — C# cơ bản","\u002Fphong-van\u002Flo-trinh\u002Fngay-1-csharp-co-ban","1.phong-van\u002F2.lo-trinh\u002F1.ngay-1-csharp-co-ban","i-lucide-1",{"title":37,"path":38,"stem":39,"icon":40},"Ngày 2 — OOP và SOLID","\u002Fphong-van\u002Flo-trinh\u002Fngay-2-oop-solid","1.phong-van\u002F2.lo-trinh\u002F2.ngay-2-oop-solid","i-lucide-2",{"title":42,"path":43,"stem":44,"icon":45},"Ngày 3 — C# nâng cao","\u002Fphong-van\u002Flo-trinh\u002Fngay-3-csharp-nang-cao","1.phong-van\u002F2.lo-trinh\u002F3.ngay-3-csharp-nang-cao","i-lucide-3",{"title":47,"path":48,"stem":49,"icon":50},"Ngày 4 — Database và SQL","\u002Fphong-van\u002Flo-trinh\u002Fngay-4-database-sql","1.phong-van\u002F2.lo-trinh\u002F4.ngay-4-database-sql","i-lucide-4",{"title":52,"path":53,"stem":54,"icon":55},"Ngày 5 — ASP.NET Core và EF Core","\u002Fphong-van\u002Flo-trinh\u002Fngay-5-aspnet-efcore","1.phong-van\u002F2.lo-trinh\u002F5.ngay-5-aspnet-efcore","i-lucide-5",{"title":57,"path":58,"stem":59,"icon":60},"Ngày 6 — DSA coding","\u002Fphong-van\u002Flo-trinh\u002Fngay-6-dsa","1.phong-van\u002F2.lo-trinh\u002F6.ngay-6-dsa","i-lucide-6",{"title":62,"path":63,"stem":64,"icon":65},"Ngày 7 — Mock và HR","\u002Fphong-van\u002Flo-trinh\u002Fngay-7-mock-interview","1.phong-van\u002F2.lo-trinh\u002F7.ngay-7-mock-interview","i-lucide-7",{"title":67,"icon":68,"path":69,"stem":70,"children":71,"page":26},"Ngân hàng câu hỏi","i-lucide-database","\u002Fphong-van\u002Fngan-hang-cau-hoi","1.phong-van\u002F3.ngan-hang-cau-hoi",[72,77,82,87,92,97,102,107],{"title":73,"path":74,"stem":75,"icon":76},"C# (60+ câu)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Fcsharp","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F1.csharp","i-lucide-hash",{"title":78,"path":79,"stem":80,"icon":81},"OOP và SOLID (40+)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Foop","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F2.oop","i-lucide-shapes",{"title":83,"path":84,"stem":85,"icon":86},"SQL (40+)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Fsql","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F3.sql","i-lucide-database-zap",{"title":88,"path":89,"stem":90,"icon":91},"ASP.NET và EF (50+)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Faspnet-ef","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F4.aspnet-ef","i-lucide-server",{"title":93,"path":94,"stem":95,"icon":96},"DSA và Coding (30+)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Fdsa-coding","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F5.dsa-coding","i-lucide-binary",{"title":98,"path":99,"stem":100,"icon":101},"HR và Soft skill (25+)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Fhr-behavior","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F6.hr-behavior","i-lucide-users",{"title":103,"path":104,"stem":105,"icon":106},"Design Pattern (55+)","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Fdesign-pattern","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F7.design-pattern","i-lucide-puzzle",{"title":108,"path":109,"stem":110,"icon":91},"ASP.NET MVC và Web API","\u002Fphong-van\u002Fngan-hang-cau-hoi\u002Faspnet-mvc-webapi","1.phong-van\u002F3.ngan-hang-cau-hoi\u002F8.aspnet-mvc-webapi",{"title":112,"itle":113,"icon":114,"path":115,"stem":116,"children":117,"page":26},"Tai Lieu","Tài liệu và Cheat sheet","i-lucide-book-marked","\u002Fphong-van\u002Ftai-lieu","1.phong-van\u002F4.tai-lieu",[118,123],{"title":119,"path":120,"stem":121,"icon":122},"Cheat Sheet","\u002Fphong-van\u002Ftai-lieu\u002Fcheatsheet","1.phong-van\u002F4.tai-lieu\u002F1.cheatsheet","i-lucide-clipboard-list",{"title":124,"path":125,"stem":126,"icon":127},"Nguồn tham khảo","\u002Fphong-van\u002Ftai-lieu\u002Fnguon","1.phong-van\u002F4.tai-lieu\u002F2.nguon","i-lucide-link",{"title":129,"icon":130,"path":131,"stem":132,"children":133,"page":26},"Phỏng vấn English","i-lucide-globe","\u002Fphong-van\u002Fenglish-interview","1.phong-van\u002F5.english-interview",[134,138,143,148],{"title":135,"path":136,"stem":137,"icon":130},"Tổng quan English","\u002Fphong-van\u002Fenglish-interview\u002Ftong-quan","1.phong-van\u002F5.english-interview\u002F1.tong-quan",{"title":139,"path":140,"stem":141,"icon":142},"Vocabulary","\u002Fphong-van\u002Fenglish-interview\u002Fvocabulary","1.phong-van\u002F5.english-interview\u002F2.vocabulary","i-lucide-book-a",{"title":144,"path":145,"stem":146,"icon":147},"Technical Q và A (EN)","\u002Fphong-van\u002Fenglish-interview\u002Ftechnical-en","1.phong-van\u002F5.english-interview\u002F3.technical-en","i-lucide-code",{"title":149,"path":150,"stem":151,"icon":152},"Behavioral (EN)","\u002Fphong-van\u002Fenglish-interview\u002Fbehavioral-en","1.phong-van\u002F5.english-interview\u002F4.behavioral-en","i-lucide-message-square-text",{"title":154,"icon":155,"path":156,"stem":157,"children":158},"Tự học","i-lucide-graduation-cap","\u002Ftu-hoc","2.tu-hoc",[159,161,287,310,333,357,384],{"title":154,"path":156,"stem":160,"icon":155},"2.tu-hoc\u002Findex",{"title":162,"icon":68,"path":163,"stem":164,"children":165},"Database","\u002Ftu-hoc\u002Fdatabase","2.tu-hoc\u002F1.database\u002Findex",[166,167,197,222,243,248],{"title":162,"path":163,"stem":164,"icon":68},{"title":168,"icon":91,"path":169,"stem":170,"children":171,"page":26},"SQL Server","\u002Ftu-hoc\u002Fdatabase\u002Fsql-server","2.tu-hoc\u002F1.database\u002F2.sql-server",[172,177,182,187,192],{"title":173,"path":174,"stem":175,"icon":176},"1. Giới thiệu và Cài đặt","\u002Ftu-hoc\u002Fdatabase\u002Fsql-server\u002Fgioi-thieu-cai-dat","2.tu-hoc\u002F1.database\u002F2.sql-server\u002F1.gioi-thieu-cai-dat","i-lucide-play-circle",{"title":178,"path":179,"stem":180,"icon":181},"2. Cú pháp cơ bản","\u002Ftu-hoc\u002Fdatabase\u002Fsql-server\u002Fcu-phap-co-ban","2.tu-hoc\u002F1.database\u002F2.sql-server\u002F2.cu-phap-co-ban","i-lucide-code-2",{"title":183,"path":184,"stem":185,"icon":186},"3. Truy vấn nâng cao","\u002Ftu-hoc\u002Fdatabase\u002Fsql-server\u002Ftruy-van-nang-cao","2.tu-hoc\u002F1.database\u002F2.sql-server\u002F3.truy-van-nang-cao","i-lucide-git-merge",{"title":188,"path":189,"stem":190,"icon":191},"4. Đặc thù và Performance","\u002Ftu-hoc\u002Fdatabase\u002Fsql-server\u002Fdac-thu-performance","2.tu-hoc\u002F1.database\u002F2.sql-server\u002F4.dac-thu-performance","i-lucide-gauge",{"title":193,"path":194,"stem":195,"icon":196},"5. Q và A phỏng vấn","\u002Ftu-hoc\u002Fdatabase\u002Fsql-server\u002Fcau-hoi-phong-van","2.tu-hoc\u002F1.database\u002F2.sql-server\u002F5.cau-hoi-phong-van","i-lucide-message-circle-question",{"title":198,"icon":199,"path":200,"stem":201,"children":202,"page":26},"PostgreSQL","i-simple-icons-postgresql","\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql","2.tu-hoc\u002F1.database\u002F3.postgresql",[203,206,209,212,215,218],{"title":173,"path":204,"stem":205,"icon":176},"\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql\u002Fgioi-thieu-cai-dat","2.tu-hoc\u002F1.database\u002F3.postgresql\u002F1.gioi-thieu-cai-dat",{"title":178,"path":207,"stem":208,"icon":181},"\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql\u002Fcu-phap-co-ban","2.tu-hoc\u002F1.database\u002F3.postgresql\u002F2.cu-phap-co-ban",{"title":183,"path":210,"stem":211,"icon":186},"\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql\u002Ftruy-van-nang-cao","2.tu-hoc\u002F1.database\u002F3.postgresql\u002F3.truy-van-nang-cao",{"title":188,"path":213,"stem":214,"icon":191},"\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql\u002Fdac-thu-performance","2.tu-hoc\u002F1.database\u002F3.postgresql\u002F4.dac-thu-performance",{"title":193,"path":216,"stem":217,"icon":196},"\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql\u002Fcau-hoi-phong-van","2.tu-hoc\u002F1.database\u002F3.postgresql\u002F5.cau-hoi-phong-van",{"title":219,"path":220,"stem":221,"icon":96},"6. Tích hợp C# và Postgres","\u002Ftu-hoc\u002Fdatabase\u002Fpostgresql\u002Ftich-hop-csharp","2.tu-hoc\u002F1.database\u002F3.postgresql\u002F6.tich-hop-csharp",{"title":223,"icon":224,"path":225,"stem":226,"children":227,"page":26},"MySQL","i-simple-icons-mysql","\u002Ftu-hoc\u002Fdatabase\u002Fmysql","2.tu-hoc\u002F1.database\u002F4.mysql",[228,231,234,237,240],{"title":173,"path":229,"stem":230,"icon":176},"\u002Ftu-hoc\u002Fdatabase\u002Fmysql\u002Fgioi-thieu-cai-dat","2.tu-hoc\u002F1.database\u002F4.mysql\u002F1.gioi-thieu-cai-dat",{"title":178,"path":232,"stem":233,"icon":181},"\u002Ftu-hoc\u002Fdatabase\u002Fmysql\u002Fcu-phap-co-ban","2.tu-hoc\u002F1.database\u002F4.mysql\u002F2.cu-phap-co-ban",{"title":183,"path":235,"stem":236,"icon":186},"\u002Ftu-hoc\u002Fdatabase\u002Fmysql\u002Ftruy-van-nang-cao","2.tu-hoc\u002F1.database\u002F4.mysql\u002F3.truy-van-nang-cao",{"title":188,"path":238,"stem":239,"icon":191},"\u002Ftu-hoc\u002Fdatabase\u002Fmysql\u002Fdac-thu-performance","2.tu-hoc\u002F1.database\u002F4.mysql\u002F4.dac-thu-performance",{"title":193,"path":241,"stem":242,"icon":196},"\u002Ftu-hoc\u002Fdatabase\u002Fmysql\u002Fcau-hoi-phong-van","2.tu-hoc\u002F1.database\u002F4.mysql\u002F5.cau-hoi-phong-van",{"title":244,"path":245,"stem":246,"icon":247},"So sánh 3 RDBMS","\u002Ftu-hoc\u002Fdatabase\u002Fso-sanh","2.tu-hoc\u002F1.database\u002F5.so-sanh","i-lucide-columns-3",{"title":249,"path":250,"stem":251,"children":252,"icon":254},"Supabase (BaaS)","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase","2.tu-hoc\u002F1.database\u002F6.supabase\u002Findex",[253,255,259,264,269,274,278,283],{"title":249,"path":250,"stem":251,"icon":254},"i-simple-icons-supabase",{"title":256,"path":257,"stem":258,"icon":68},"1. Database","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Fdatabase","2.tu-hoc\u002F1.database\u002F6.supabase\u002F1.database",{"title":260,"path":261,"stem":262,"icon":263},"2. Authentication","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Fauthentication","2.tu-hoc\u002F1.database\u002F6.supabase\u002F2.authentication","i-lucide-key",{"title":265,"path":266,"stem":267,"icon":268},"3. Storage & Realtime","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Fstorage-realtime","2.tu-hoc\u002F1.database\u002F6.supabase\u002F3.storage-realtime","i-lucide-zap",{"title":270,"path":271,"stem":272,"icon":273},"4. Edge Functions & AI","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Ffunctions-ai","2.tu-hoc\u002F1.database\u002F6.supabase\u002F4.functions-ai","i-lucide-brain",{"title":275,"path":276,"stem":277,"icon":181},"5. API & SDKs","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Fapi-sdk","2.tu-hoc\u002F1.database\u002F6.supabase\u002F5.api-sdk",{"title":279,"path":280,"stem":281,"icon":282},"6. Platform & Deploy","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Fplatform-deploy","2.tu-hoc\u002F1.database\u002F6.supabase\u002F6.platform-deploy","i-lucide-cloud",{"title":284,"path":285,"stem":286,"icon":196},"7. Interview & Q&A","\u002Ftu-hoc\u002Fdatabase\u002Fsupabase\u002Finterview","2.tu-hoc\u002F1.database\u002F6.supabase\u002F7.interview",{"title":288,"icon":289,"path":290,"stem":291,"children":292},"TypeScript","i-simple-icons-typescript","\u002Ftu-hoc\u002Ftypescript","2.tu-hoc\u002F2.typescript\u002Findex",[293,294,297,301,306],{"title":288,"path":290,"stem":291,"icon":289},{"title":173,"path":295,"stem":296,"icon":176},"\u002Ftu-hoc\u002Ftypescript\u002Fgioi-thieu-cai-dat","2.tu-hoc\u002F2.typescript\u002F1.gioi-thieu-cai-dat",{"title":298,"path":299,"stem":300,"icon":81},"2. Type System","\u002Ftu-hoc\u002Ftypescript\u002Ftype-system","2.tu-hoc\u002F2.typescript\u002F2.type-system",{"title":302,"path":303,"stem":304,"icon":305},"3. Advanced types","\u002Ftu-hoc\u002Ftypescript\u002Fadvanced-types","2.tu-hoc\u002F2.typescript\u002F3.advanced-types","i-lucide-braces",{"title":307,"path":308,"stem":309,"icon":196},"4. Q và A phỏng vấn","\u002Ftu-hoc\u002Ftypescript\u002Fcau-hoi-phong-van","2.tu-hoc\u002F2.typescript\u002F4.cau-hoi-phong-van",{"title":311,"icon":81,"path":312,"stem":313,"children":314},"OOP","\u002Ftu-hoc\u002Foop","2.tu-hoc\u002F3.oop\u002Findex",[315,316,321,326,330],{"title":311,"path":312,"stem":313,"icon":81},{"title":317,"path":318,"stem":319,"icon":320},"1. Bốn tính chất","\u002Ftu-hoc\u002Foop\u002F4-tinh-chat","2.tu-hoc\u002F3.oop\u002F1.4-tinh-chat","i-lucide-circle-dot",{"title":322,"path":323,"stem":324,"icon":325},"2. SOLID","\u002Ftu-hoc\u002Foop\u002Fsolid","2.tu-hoc\u002F3.oop\u002F2.solid","i-lucide-shield",{"title":327,"path":328,"stem":329,"icon":106},"3. Design Pattern","\u002Ftu-hoc\u002Foop\u002Fdesign-pattern","2.tu-hoc\u002F3.oop\u002F3.design-pattern",{"title":307,"path":331,"stem":332,"icon":196},"\u002Ftu-hoc\u002Foop\u002Fcau-hoi-phong-van","2.tu-hoc\u002F3.oop\u002F4.cau-hoi-phong-van",{"title":334,"icon":305,"path":335,"stem":336,"children":337},"Generic","\u002Ftu-hoc\u002Fgeneric","2.tu-hoc\u002F4.generic\u002Findex",[338,339,344,349,354],{"title":334,"path":335,"stem":336,"icon":305},{"title":340,"path":341,"stem":342,"icon":343},"1. Khái niệm và cú pháp","\u002Ftu-hoc\u002Fgeneric\u002Fkhai-niem","2.tu-hoc\u002F4.generic\u002F1.khai-niem","i-lucide-book",{"title":345,"path":346,"stem":347,"icon":348},"2. Constraint và Variance","\u002Ftu-hoc\u002Fgeneric\u002Fconstraint-variance","2.tu-hoc\u002F4.generic\u002F2.constraint-variance","i-lucide-filter",{"title":350,"path":351,"stem":352,"icon":353},"3. C# vs TS","\u002Ftu-hoc\u002Fgeneric\u002Fso-sanh","2.tu-hoc\u002F4.generic\u002F3.so-sanh","i-lucide-git-compare",{"title":307,"path":355,"stem":356,"icon":196},"\u002Ftu-hoc\u002Fgeneric\u002Fcau-hoi-phong-van","2.tu-hoc\u002F4.generic\u002F4.cau-hoi-phong-van",{"title":358,"icon":359,"path":360,"stem":361,"children":362},"Nuxt 4","i-simple-icons-nuxt","\u002Ftu-hoc\u002Fnuxt-4","2.tu-hoc\u002F5.nuxt-4\u002Findex",[363,364,367,372,376,379],{"title":358,"path":360,"stem":361,"icon":359},{"title":173,"path":365,"stem":366,"icon":176},"\u002Ftu-hoc\u002Fnuxt-4\u002Fgioi-thieu-cai-dat","2.tu-hoc\u002F5.nuxt-4\u002F1.gioi-thieu-cai-dat",{"title":368,"path":369,"stem":370,"icon":371},"2. Routing và Rendering","\u002Ftu-hoc\u002Fnuxt-4\u002Frouting-rendering","2.tu-hoc\u002F5.nuxt-4\u002F2.routing-rendering","i-lucide-route",{"title":373,"path":374,"stem":375,"icon":91},"3. Data và Server","\u002Ftu-hoc\u002Fnuxt-4\u002Fdata-server","2.tu-hoc\u002F5.nuxt-4\u002F3.data-server",{"title":307,"path":377,"stem":378,"icon":196},"\u002Ftu-hoc\u002Fnuxt-4\u002Fcau-hoi-phong-van","2.tu-hoc\u002F5.nuxt-4\u002F4.cau-hoi-phong-van",{"title":380,"path":381,"stem":382,"icon":383},"5. Practical Todo App","\u002Ftu-hoc\u002Fnuxt-4\u002Fpractical-todo-app","2.tu-hoc\u002F5.nuxt-4\u002F5.practical-todo-app","i-lucide-check-square",{"title":385,"path":386,"stem":387,"children":388,"icon":390},"Tailwind CSS","\u002Ftu-hoc\u002Ftailwindcss","2.tu-hoc\u002F6.tailwindcss\u002Findex",[389,391,396,401,406,411,416],{"title":385,"path":386,"stem":387,"icon":390},"i-simple-icons-tailwindcss",{"title":392,"path":393,"stem":394,"icon":395},"1. Layout & Spacing","\u002Ftu-hoc\u002Ftailwindcss\u002Flayout-spacing","2.tu-hoc\u002F6.tailwindcss\u002F1.layout-spacing","i-lucide-layout",{"title":397,"path":398,"stem":399,"icon":400},"2. Typography & Colors","\u002Ftu-hoc\u002Ftailwindcss\u002Ftypography-colors","2.tu-hoc\u002F6.tailwindcss\u002F2.typography-colors","i-lucide-type",{"title":402,"path":403,"stem":404,"icon":405},"3. Interactive & Effects","\u002Ftu-hoc\u002Ftailwindcss\u002Finteractive-effects","2.tu-hoc\u002F6.tailwindcss\u002F3.interactive-effects","i-lucide-sparkles",{"title":407,"path":408,"stem":409,"icon":410},"4. Responsive & Dark Mode","\u002Ftu-hoc\u002Ftailwindcss\u002Fresponsive-darkmode","2.tu-hoc\u002F6.tailwindcss\u002F4.responsive-darkmode","i-lucide-monitor-smartphone",{"title":412,"path":413,"stem":414,"icon":415},"5. Customization & Theme","\u002Ftu-hoc\u002Ftailwindcss\u002Fcustomization","2.tu-hoc\u002F6.tailwindcss\u002F5.customization","i-lucide-settings-2",{"title":417,"path":418,"stem":419,"icon":181},"6. Practical Projects","\u002Ftu-hoc\u002Ftailwindcss\u002Fpractical-projects","2.tu-hoc\u002F6.tailwindcss\u002F6.practical-projects",{"id":421,"title":422,"body":423,"description":3063,"extension":3064,"links":3065,"meta":3066,"navigation":3067,"path":207,"seo":3068,"stem":208,"__hash__":3069},"docs\u002F2.tu-hoc\u002F1.database\u002F3.postgresql\u002F2.cu-phap-co-ban.md","Cú pháp PostgreSQL cơ bản",{"type":424,"value":425,"toc":3019},"minimark",[426,431,436,585,632,636,687,730,734,802,829,833,1068,1082,1086,1161,1165,1283,1287,1335,1345,1349,1353,1373,1383,1413,1426,1430,1455,1462,1518,1539,1557,1561,1610,1614,1648,1652,1672,1676,1719,1723,1727,1782,1786,1835,1839,1949,1977,1981,2034,2038,2045,2085,2098,2102,2106,2150,2154,2229,2233,2283,2308,2312,2319,2326,2339,2369,2374,2439,2491,2523,2556,2564,2570,2595,2612,2659,2684,2692,2697,2711,2802,2812,2831,2837,2866,2874,2878,2900,2949,2954,2996,3005,3008,3015],[427,428,430],"h2",{"id":429},"_1-kiểu-dữ-liệu-đa-dạng-hơn-sql-server","1. Kiểu dữ liệu — đa dạng hơn SQL Server",[432,433,435],"h3",{"id":434},"numeric","Numeric",[437,438,439,458],"table",{},[440,441,442],"thead",{},[443,444,445,449,452,455],"tr",{},[446,447,448],"th",{},"Kiểu",[446,450,451],{},"Range",[446,453,454],{},"Storage",[446,456,457],{},"Khi nào",[459,460,461,479,499,515,537,552,566],"tbody",{},[443,462,463,470,473,476],{},[464,465,466],"td",{},[467,468,469],"code",{},"SMALLINT",[464,471,472],{},"-32K → 32K",[464,474,475],{},"2 bytes",[464,477,478],{},"Số nhỏ",[443,480,481,490,493,496],{},[464,482,483,486,487],{},[467,484,485],{},"INTEGER"," \u002F ",[467,488,489],{},"INT",[464,491,492],{},"-2.1B → 2.1B",[464,494,495],{},"4 bytes",[464,497,498],{},"ID nhỏ",[443,500,501,506,509,512],{},[464,502,503],{},[467,504,505],{},"BIGINT",[464,507,508],{},"±9.2 × 10¹⁸",[464,510,511],{},"8 bytes",[464,513,514],{},"ID lớn, timestamp",[443,516,517,525,528,531],{},[464,518,519,486,522],{},[467,520,521],{},"DECIMAL(p, s)",[467,523,524],{},"NUMERIC(p, s)",[464,526,527],{},"Chính xác",[464,529,530],{},"Variable",[464,532,533],{},[534,535,536],"strong",{},"Tiền",[443,538,539,544,547,549],{},[464,540,541],{},[467,542,543],{},"REAL",[464,545,546],{},"Float 32-bit",[464,548,495],{},[464,550,551],{},"Khoa học",[443,553,554,559,562,564],{},[464,555,556],{},[467,557,558],{},"DOUBLE PRECISION",[464,560,561],{},"Float 64-bit",[464,563,511],{},[464,565,551],{},[443,567,568,576,579,582],{},[464,569,570,486,573],{},[467,571,572],{},"SERIAL",[467,574,575],{},"BIGSERIAL",[464,577,578],{},"Auto-increment",[464,580,581],{},"4\u002F8",[464,583,584],{},"Legacy — giờ ưu tiên IDENTITY",[586,587,592],"pre",{"className":588,"code":589,"language":590,"meta":591,"style":591},"language-sql shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","-- Postgres recommend cú pháp IDENTITY chuẩn ANSI hơn SERIAL\nCREATE TABLE products (\n    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n    name TEXT NOT NULL,\n    price NUMERIC(18, 2) NOT NULL CHECK (price >= 0)\n);\n","sql","",[467,593,594,602,608,614,620,626],{"__ignoreMap":591},[595,596,599],"span",{"class":597,"line":598},"line",1,[595,600,601],{},"-- Postgres recommend cú pháp IDENTITY chuẩn ANSI hơn SERIAL\n",[595,603,605],{"class":597,"line":604},2,[595,606,607],{},"CREATE TABLE products (\n",[595,609,611],{"class":597,"line":610},3,[595,612,613],{},"    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n",[595,615,617],{"class":597,"line":616},4,[595,618,619],{},"    name TEXT NOT NULL,\n",[595,621,623],{"class":597,"line":622},5,[595,624,625],{},"    price NUMERIC(18, 2) NOT NULL CHECK (price >= 0)\n",[595,627,629],{"class":597,"line":628},6,[595,630,631],{},");\n",[432,633,635],{"id":634},"chuỗi","Chuỗi",[437,637,638,647],{},[440,639,640],{},[443,641,642,644],{},[446,643,448],{},[446,645,646],{},"Ghi chú",[459,648,649,659,673],{},[443,650,651,656],{},[464,652,653],{},[467,654,655],{},"CHAR(n)",[464,657,658],{},"Cố định n char, pad space. Hiếm dùng.",[443,660,661,666],{},[464,662,663],{},[467,664,665],{},"VARCHAR(n)",[464,667,668,669,672],{},"Giới hạn n. ",[534,670,671],{},"Không nhanh hơn TEXT"," ở Postgres.",[443,674,675,680],{},[464,676,677],{},[467,678,679],{},"TEXT",[464,681,682,683,686],{},"Không giới hạn. ",[534,684,685],{},"Mặc định nên dùng",".",[688,689,692,704],"callout",{"color":690,"icon":691},"warning","i-lucide-star",[693,694,695,696,699,700],"p",{},"⭐ ",[534,697,698],{},"Câu PV đặc thù Postgres:"," ",[701,702,703],"em",{},"\"VARCHAR(255) vs TEXT — chọn cái nào?\"",[705,706,707],"blockquote",{},[693,708,709,710,713,714,716,717,719,720,722,723,726,727,729],{},"\"Khác SQL Server\u002FMySQL, Postgres ",[534,711,712],{},"không phân biệt performance"," giữa ",[467,715,665],{}," và ",[467,718,679],{},". Cả 2 đều lưu cùng cách (TOAST cho data lớn). Dùng ",[467,721,665],{}," chỉ để ",[534,724,725],{},"enforce length limit"," ở DB. Còn không thì ",[467,728,679],{}," cho linh hoạt.\"",[432,731,733],{"id":732},"thời-gian","Thời gian",[437,735,736,745],{},[440,737,738],{},[443,739,740,742],{},[446,741,448],{},[446,743,744],{},"Ý nghĩa",[459,746,747,757,767,780,792],{},[443,748,749,754],{},[464,750,751],{},[467,752,753],{},"DATE",[464,755,756],{},"Ngày, không giờ",[443,758,759,764],{},[464,760,761],{},[467,762,763],{},"TIME",[464,765,766],{},"Giờ, không ngày",[443,768,769,774],{},[464,770,771],{},[467,772,773],{},"TIMESTAMP",[464,775,776,777],{},"Ngày + giờ, ",[534,778,779],{},"không timezone",[443,781,782,787],{},[464,783,784],{},[467,785,786],{},"TIMESTAMPTZ",[464,788,789,790,686],{},"Ngày + giờ + timezone, lưu UTC. ",[534,791,685],{},[443,793,794,799],{},[464,795,796],{},[467,797,798],{},"INTERVAL",[464,800,801],{},"Khoảng thời gian",[688,803,805,814],{"color":804,"icon":268},"primary",[693,806,807,810,811,813],{},[534,808,809],{},"Best practice 2025:"," luôn ",[467,812,786],{}," cho mọi cột thời gian user-facing. Postgres lưu UTC, convert theo session timezone khi đọc.",[586,815,817],{"className":588,"code":816,"language":590,"meta":591,"style":591},"SET TIMEZONE TO 'Asia\u002FHo_Chi_Minh';\nSELECT NOW();   -- output sẽ ở +07:00\n",[467,818,819,824],{"__ignoreMap":591},[595,820,821],{"class":597,"line":598},[595,822,823],{},"SET TIMEZONE TO 'Asia\u002FHo_Chi_Minh';\n",[595,825,826],{"class":597,"line":604},[595,827,828],{},"SELECT NOW();   -- output sẽ ở +07:00\n",[432,830,832],{"id":831},"kiểu-postgres-đặc-trưng","Kiểu Postgres đặc trưng",[586,834,836],{"className":588,"code":835,"language":590,"meta":591,"style":591},"-- Array\nCREATE TABLE posts (\n    id BIGSERIAL PRIMARY KEY,\n    title TEXT,\n    tags TEXT[]                            -- array of TEXT\n);\nINSERT INTO posts (title, tags) VALUES ('Hello', ARRAY['vue', 'nuxt']);\nSELECT * FROM posts WHERE 'vue' = ANY(tags);\n\n-- JSON \u002F JSONB (JSONB nén nhị phân, query nhanh, index được)\nCREATE TABLE events (\n    id BIGSERIAL PRIMARY KEY,\n    payload JSONB NOT NULL\n);\nINSERT INTO events (payload)\nVALUES ('{\"type\": \"click\", \"user\": \"alice\", \"tags\": [\"promo\"]}');\n\nSELECT payload->>'type' AS event_type,\n       payload->'user' AS user_json\nFROM events;\n\n-- UUID — Postgres 18 có uuidv7() built-in\nCREATE TABLE accounts (\n    id UUID PRIMARY KEY DEFAULT uuidv7(),    -- 18+\n    -- DEFAULT gen_random_uuid() cho version \u003C 18\n    email TEXT UNIQUE\n);\n\n-- INET \u002F CIDR\nCREATE TABLE logs (\n    ip INET,\n    subnet CIDR\n);\n\n-- Range type\nCREATE TABLE reservations (\n    id BIGSERIAL PRIMARY KEY,\n    room_id INT,\n    during TSRANGE NOT NULL,\n    EXCLUDE USING GIST (room_id WITH =, during WITH &&)  -- chống overlap!\n);\n",[467,837,838,843,848,853,858,863,867,873,879,886,892,898,903,909,914,920,926,931,937,943,949,954,960,966,972,978,984,989,994,1000,1006,1012,1018,1023,1028,1034,1040,1045,1051,1057,1063],{"__ignoreMap":591},[595,839,840],{"class":597,"line":598},[595,841,842],{},"-- Array\n",[595,844,845],{"class":597,"line":604},[595,846,847],{},"CREATE TABLE posts (\n",[595,849,850],{"class":597,"line":610},[595,851,852],{},"    id BIGSERIAL PRIMARY KEY,\n",[595,854,855],{"class":597,"line":616},[595,856,857],{},"    title TEXT,\n",[595,859,860],{"class":597,"line":622},[595,861,862],{},"    tags TEXT[]                            -- array of TEXT\n",[595,864,865],{"class":597,"line":628},[595,866,631],{},[595,868,870],{"class":597,"line":869},7,[595,871,872],{},"INSERT INTO posts (title, tags) VALUES ('Hello', ARRAY['vue', 'nuxt']);\n",[595,874,876],{"class":597,"line":875},8,[595,877,878],{},"SELECT * FROM posts WHERE 'vue' = ANY(tags);\n",[595,880,882],{"class":597,"line":881},9,[595,883,885],{"emptyLinePlaceholder":884},true,"\n",[595,887,889],{"class":597,"line":888},10,[595,890,891],{},"-- JSON \u002F JSONB (JSONB nén nhị phân, query nhanh, index được)\n",[595,893,895],{"class":597,"line":894},11,[595,896,897],{},"CREATE TABLE events (\n",[595,899,901],{"class":597,"line":900},12,[595,902,852],{},[595,904,906],{"class":597,"line":905},13,[595,907,908],{},"    payload JSONB NOT NULL\n",[595,910,912],{"class":597,"line":911},14,[595,913,631],{},[595,915,917],{"class":597,"line":916},15,[595,918,919],{},"INSERT INTO events (payload)\n",[595,921,923],{"class":597,"line":922},16,[595,924,925],{},"VALUES ('{\"type\": \"click\", \"user\": \"alice\", \"tags\": [\"promo\"]}');\n",[595,927,929],{"class":597,"line":928},17,[595,930,885],{"emptyLinePlaceholder":884},[595,932,934],{"class":597,"line":933},18,[595,935,936],{},"SELECT payload->>'type' AS event_type,\n",[595,938,940],{"class":597,"line":939},19,[595,941,942],{},"       payload->'user' AS user_json\n",[595,944,946],{"class":597,"line":945},20,[595,947,948],{},"FROM events;\n",[595,950,952],{"class":597,"line":951},21,[595,953,885],{"emptyLinePlaceholder":884},[595,955,957],{"class":597,"line":956},22,[595,958,959],{},"-- UUID — Postgres 18 có uuidv7() built-in\n",[595,961,963],{"class":597,"line":962},23,[595,964,965],{},"CREATE TABLE accounts (\n",[595,967,969],{"class":597,"line":968},24,[595,970,971],{},"    id UUID PRIMARY KEY DEFAULT uuidv7(),    -- 18+\n",[595,973,975],{"class":597,"line":974},25,[595,976,977],{},"    -- DEFAULT gen_random_uuid() cho version \u003C 18\n",[595,979,981],{"class":597,"line":980},26,[595,982,983],{},"    email TEXT UNIQUE\n",[595,985,987],{"class":597,"line":986},27,[595,988,631],{},[595,990,992],{"class":597,"line":991},28,[595,993,885],{"emptyLinePlaceholder":884},[595,995,997],{"class":597,"line":996},29,[595,998,999],{},"-- INET \u002F CIDR\n",[595,1001,1003],{"class":597,"line":1002},30,[595,1004,1005],{},"CREATE TABLE logs (\n",[595,1007,1009],{"class":597,"line":1008},31,[595,1010,1011],{},"    ip INET,\n",[595,1013,1015],{"class":597,"line":1014},32,[595,1016,1017],{},"    subnet CIDR\n",[595,1019,1021],{"class":597,"line":1020},33,[595,1022,631],{},[595,1024,1026],{"class":597,"line":1025},34,[595,1027,885],{"emptyLinePlaceholder":884},[595,1029,1031],{"class":597,"line":1030},35,[595,1032,1033],{},"-- Range type\n",[595,1035,1037],{"class":597,"line":1036},36,[595,1038,1039],{},"CREATE TABLE reservations (\n",[595,1041,1043],{"class":597,"line":1042},37,[595,1044,852],{},[595,1046,1048],{"class":597,"line":1047},38,[595,1049,1050],{},"    room_id INT,\n",[595,1052,1054],{"class":597,"line":1053},39,[595,1055,1056],{},"    during TSRANGE NOT NULL,\n",[595,1058,1060],{"class":597,"line":1059},40,[595,1061,1062],{},"    EXCLUDE USING GIST (room_id WITH =, during WITH &&)  -- chống overlap!\n",[595,1064,1066],{"class":597,"line":1065},41,[595,1067,631],{},[688,1069,695,1070,1077,1078,1081],{"color":690,"icon":691},[534,1071,1072,1073,1076],{},"Postgres feature hot — ",[467,1074,1075],{},"EXCLUDE"," constraint:"," ngăn 2 row có range overlap cùng ",[467,1079,1080],{},"room_id",". Không hệ DB nào khác có ngon thế này.",[432,1083,1085],{"id":1084},"json-vs-jsonb","JSON vs JSONB",[437,1087,1088,1100],{},[440,1089,1090],{},[443,1091,1092,1094,1097],{},[446,1093],{},[446,1095,1096],{},"JSON",[446,1098,1099],{},"JSONB",[459,1101,1102,1113,1124,1138,1149],{},[443,1103,1104,1107,1110],{},[464,1105,1106],{},"Lưu",[464,1108,1109],{},"Text chuẩn",[464,1111,1112],{},"Nhị phân nén",[443,1114,1115,1118,1121],{},[464,1116,1117],{},"Insert",[464,1119,1120],{},"Nhanh",[464,1122,1123],{},"Chậm hơn chút (parse + encode)",[443,1125,1126,1129,1132],{},[464,1127,1128],{},"Query\u002FIndex",[464,1130,1131],{},"Chậm",[464,1133,1134,1137],{},[534,1135,1136],{},"Nhanh hơn nhiều",", hỗ trợ GIN index",[443,1139,1140,1143,1146],{},[464,1141,1142],{},"Giữ whitespace, key order",[464,1144,1145],{},"✅",[464,1147,1148],{},"❌",[443,1150,1151,1154,1157],{},[464,1152,1153],{},"Khuyến nghị",[464,1155,1156],{},"Audit log raw",[464,1158,1159],{},[534,1160,685],{},[427,1162,1164],{"id":1163},"_2-ddl-data-definition","2. DDL — Data Definition",[586,1166,1168],{"className":588,"code":1167,"language":590,"meta":591,"style":591},"CREATE TABLE users (\n    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n    email TEXT NOT NULL UNIQUE,\n    age INT CHECK (age BETWEEN 0 AND 150),\n    status TEXT NOT NULL DEFAULT 'active',\n    country_id INT REFERENCES countries(id) ON DELETE SET NULL,\n    metadata JSONB DEFAULT '{}',\n    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n);\n\n-- Add column\nALTER TABLE users ADD COLUMN avatar_url TEXT;\n\n-- Drop column\nALTER TABLE users DROP COLUMN avatar_url;\n\n-- Rename\nALTER TABLE users RENAME COLUMN status TO state;\n\n-- Change type\nALTER TABLE users ALTER COLUMN age TYPE BIGINT;\n\n-- Drop table\nDROP TABLE IF EXISTS users CASCADE;\n",[467,1169,1170,1175,1179,1184,1189,1194,1199,1204,1209,1213,1217,1222,1227,1231,1236,1241,1245,1250,1255,1259,1264,1269,1273,1278],{"__ignoreMap":591},[595,1171,1172],{"class":597,"line":598},[595,1173,1174],{},"CREATE TABLE users (\n",[595,1176,1177],{"class":597,"line":604},[595,1178,613],{},[595,1180,1181],{"class":597,"line":610},[595,1182,1183],{},"    email TEXT NOT NULL UNIQUE,\n",[595,1185,1186],{"class":597,"line":616},[595,1187,1188],{},"    age INT CHECK (age BETWEEN 0 AND 150),\n",[595,1190,1191],{"class":597,"line":622},[595,1192,1193],{},"    status TEXT NOT NULL DEFAULT 'active',\n",[595,1195,1196],{"class":597,"line":628},[595,1197,1198],{},"    country_id INT REFERENCES countries(id) ON DELETE SET NULL,\n",[595,1200,1201],{"class":597,"line":869},[595,1202,1203],{},"    metadata JSONB DEFAULT '{}',\n",[595,1205,1206],{"class":597,"line":875},[595,1207,1208],{},"    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n",[595,1210,1211],{"class":597,"line":881},[595,1212,631],{},[595,1214,1215],{"class":597,"line":888},[595,1216,885],{"emptyLinePlaceholder":884},[595,1218,1219],{"class":597,"line":894},[595,1220,1221],{},"-- Add column\n",[595,1223,1224],{"class":597,"line":900},[595,1225,1226],{},"ALTER TABLE users ADD COLUMN avatar_url TEXT;\n",[595,1228,1229],{"class":597,"line":905},[595,1230,885],{"emptyLinePlaceholder":884},[595,1232,1233],{"class":597,"line":911},[595,1234,1235],{},"-- Drop column\n",[595,1237,1238],{"class":597,"line":916},[595,1239,1240],{},"ALTER TABLE users DROP COLUMN avatar_url;\n",[595,1242,1243],{"class":597,"line":922},[595,1244,885],{"emptyLinePlaceholder":884},[595,1246,1247],{"class":597,"line":928},[595,1248,1249],{},"-- Rename\n",[595,1251,1252],{"class":597,"line":933},[595,1253,1254],{},"ALTER TABLE users RENAME COLUMN status TO state;\n",[595,1256,1257],{"class":597,"line":939},[595,1258,885],{"emptyLinePlaceholder":884},[595,1260,1261],{"class":597,"line":945},[595,1262,1263],{},"-- Change type\n",[595,1265,1266],{"class":597,"line":951},[595,1267,1268],{},"ALTER TABLE users ALTER COLUMN age TYPE BIGINT;\n",[595,1270,1271],{"class":597,"line":956},[595,1272,885],{"emptyLinePlaceholder":884},[595,1274,1275],{"class":597,"line":962},[595,1276,1277],{},"-- Drop table\n",[595,1279,1280],{"class":597,"line":968},[595,1281,1282],{},"DROP TABLE IF EXISTS users CASCADE;\n",[432,1284,1286],{"id":1285},"generated-columns","Generated columns",[586,1288,1290],{"className":588,"code":1289,"language":590,"meta":591,"style":591},"-- VIRTUAL (Postgres 18 default) — tính khi đọc\nALTER TABLE products\n    ADD COLUMN price_with_tax NUMERIC(18, 2)\n    GENERATED ALWAYS AS (price * 1.1) VIRTUAL;\n\n-- STORED — lưu vật lý, index được\nALTER TABLE products\n    ADD COLUMN price_with_tax_stored NUMERIC(18, 2)\n    GENERATED ALWAYS AS (price * 1.1) STORED;\n",[467,1291,1292,1297,1302,1307,1312,1316,1321,1325,1330],{"__ignoreMap":591},[595,1293,1294],{"class":597,"line":598},[595,1295,1296],{},"-- VIRTUAL (Postgres 18 default) — tính khi đọc\n",[595,1298,1299],{"class":597,"line":604},[595,1300,1301],{},"ALTER TABLE products\n",[595,1303,1304],{"class":597,"line":610},[595,1305,1306],{},"    ADD COLUMN price_with_tax NUMERIC(18, 2)\n",[595,1308,1309],{"class":597,"line":616},[595,1310,1311],{},"    GENERATED ALWAYS AS (price * 1.1) VIRTUAL;\n",[595,1313,1314],{"class":597,"line":622},[595,1315,885],{"emptyLinePlaceholder":884},[595,1317,1318],{"class":597,"line":628},[595,1319,1320],{},"-- STORED — lưu vật lý, index được\n",[595,1322,1323],{"class":597,"line":869},[595,1324,1301],{},[595,1326,1327],{"class":597,"line":875},[595,1328,1329],{},"    ADD COLUMN price_with_tax_stored NUMERIC(18, 2)\n",[595,1331,1332],{"class":597,"line":881},[595,1333,1334],{},"    GENERATED ALWAYS AS (price * 1.1) STORED;\n",[688,1336,695,1337,1340,1341,1344],{"color":804,"icon":691},[534,1338,1339],{},"Postgres 18 changes:"," generated columns mặc định là ",[534,1342,1343],{},"VIRTUAL"," (tính khi đọc, không tốn storage). Trước đó luôn STORED.",[427,1346,1348],{"id":1347},"_3-dml","3. DML",[432,1350,1352],{"id":1351},"insert-returning-cực-kỳ-tiện","INSERT + RETURNING (cực kỳ tiện)",[586,1354,1356],{"className":588,"code":1355,"language":590,"meta":591,"style":591},"INSERT INTO users (email, age)\nVALUES ('alice@x.com', 25)\nRETURNING id, email, created_at;\n",[467,1357,1358,1363,1368],{"__ignoreMap":591},[595,1359,1360],{"class":597,"line":598},[595,1361,1362],{},"INSERT INTO users (email, age)\n",[595,1364,1365],{"class":597,"line":604},[595,1366,1367],{},"VALUES ('alice@x.com', 25)\n",[595,1369,1370],{"class":597,"line":610},[595,1371,1372],{},"RETURNING id, email, created_at;\n",[693,1374,1375,1376,716,1379,1382],{},"Postgres 18 thêm ",[467,1377,1378],{},"OLD",[467,1380,1381],{},"NEW"," trong RETURNING:",[586,1384,1386],{"className":588,"code":1385,"language":590,"meta":591,"style":591},"-- 18+ — UPDATE log diff\nUPDATE users\nSET age = age + 1\nWHERE id = 1\nRETURNING OLD.age AS old_age, NEW.age AS new_age;\n",[467,1387,1388,1393,1398,1403,1408],{"__ignoreMap":591},[595,1389,1390],{"class":597,"line":598},[595,1391,1392],{},"-- 18+ — UPDATE log diff\n",[595,1394,1395],{"class":597,"line":604},[595,1396,1397],{},"UPDATE users\n",[595,1399,1400],{"class":597,"line":610},[595,1401,1402],{},"SET age = age + 1\n",[595,1404,1405],{"class":597,"line":616},[595,1406,1407],{},"WHERE id = 1\n",[595,1409,1410],{"class":597,"line":622},[595,1411,1412],{},"RETURNING OLD.age AS old_age, NEW.age AS new_age;\n",[688,1414,695,1415,1421,1422,1425],{"color":690,"icon":691},[534,1416,1417,1420],{},[467,1418,1419],{},"RETURNING"," là tính năng cực mạnh"," — viết audit log, get ID vừa insert, biết row nào bị UPDATE\u002FDELETE. Tương đương ",[467,1423,1424],{},"OUTPUT"," của SQL Server.",[432,1427,1429],{"id":1428},"multi-row-insert","Multi-row INSERT",[586,1431,1433],{"className":588,"code":1432,"language":590,"meta":591,"style":591},"INSERT INTO products (name, price) VALUES\n('Laptop', 32000000),\n('Mouse', 800000),\n('Keyboard', 2200000);\n",[467,1434,1435,1440,1445,1450],{"__ignoreMap":591},[595,1436,1437],{"class":597,"line":598},[595,1438,1439],{},"INSERT INTO products (name, price) VALUES\n",[595,1441,1442],{"class":597,"line":604},[595,1443,1444],{},"('Laptop', 32000000),\n",[595,1446,1447],{"class":597,"line":610},[595,1448,1449],{},"('Mouse', 800000),\n",[595,1451,1452],{"class":597,"line":616},[595,1453,1454],{},"('Keyboard', 2200000);\n",[432,1456,1458,1459],{"id":1457},"upsert-on-conflict","UPSERT — ",[467,1460,1461],{},"ON CONFLICT",[586,1463,1465],{"className":588,"code":1464,"language":590,"meta":591,"style":591},"-- INSERT, nếu trùng email thì UPDATE\nINSERT INTO users (email, age)\nVALUES ('alice@x.com', 26)\nON CONFLICT (email) DO UPDATE\n    SET age = EXCLUDED.age,\n        updated_at = NOW();\n\n-- Hoặc DO NOTHING\nINSERT INTO users (email, age)\nVALUES ('alice@x.com', 26)\nON CONFLICT DO NOTHING;\n",[467,1466,1467,1472,1476,1481,1486,1491,1496,1500,1505,1509,1513],{"__ignoreMap":591},[595,1468,1469],{"class":597,"line":598},[595,1470,1471],{},"-- INSERT, nếu trùng email thì UPDATE\n",[595,1473,1474],{"class":597,"line":604},[595,1475,1362],{},[595,1477,1478],{"class":597,"line":610},[595,1479,1480],{},"VALUES ('alice@x.com', 26)\n",[595,1482,1483],{"class":597,"line":616},[595,1484,1485],{},"ON CONFLICT (email) DO UPDATE\n",[595,1487,1488],{"class":597,"line":622},[595,1489,1490],{},"    SET age = EXCLUDED.age,\n",[595,1492,1493],{"class":597,"line":628},[595,1494,1495],{},"        updated_at = NOW();\n",[595,1497,1498],{"class":597,"line":869},[595,1499,885],{"emptyLinePlaceholder":884},[595,1501,1502],{"class":597,"line":875},[595,1503,1504],{},"-- Hoặc DO NOTHING\n",[595,1506,1507],{"class":597,"line":881},[595,1508,1362],{},[595,1510,1511],{"class":597,"line":888},[595,1512,1480],{},[595,1514,1515],{"class":597,"line":894},[595,1516,1517],{},"ON CONFLICT DO NOTHING;\n",[688,1519,1520,1533],{"color":804,"icon":691},[693,1521,695,1522,699,1525,1528,1529,1532],{},[534,1523,1524],{},"Câu PV:",[701,1526,1527],{},"\"Postgres UPSERT làm sao?\""," → ",[467,1530,1531],{},"INSERT ... ON CONFLICT (key) DO UPDATE",". Đáp án này luôn được điểm.",[693,1534,1535,1538],{},[467,1536,1537],{},"EXCLUDED"," = row đang định INSERT (giúp UPDATE bằng giá trị mới).",[688,1540,1541,699,1544,1547,1548,1551,1552,686],{"color":804,"icon":268},[534,1542,1543],{},"Mới trong PostgreSQL 19:",[467,1545,1546],{},"ON CONFLICT DO SELECT"," — hành động thứ ba cho phép ",[534,1549,1550],{},"atomic get-or-create"," không dead tuple, nhanh hơn ~4x so với no-op UPDATE. Chi tiết tại ",[1553,1554,1556],"a",{"href":1555},"#81-on-conflict-do-select--atomic-get-or-create","mục 8.1",[432,1558,1560],{"id":1559},"update","UPDATE",[586,1562,1564],{"className":588,"code":1563,"language":590,"meta":591,"style":591},"UPDATE products\nSET price = price * 1.1, updated_at = NOW()\nWHERE category_id = 1;\n\n-- UPDATE từ JOIN (Postgres dùng FROM)\nUPDATE products p\nSET category_name = c.name\nFROM categories c\nWHERE c.id = p.category_id;\n",[467,1565,1566,1571,1576,1581,1585,1590,1595,1600,1605],{"__ignoreMap":591},[595,1567,1568],{"class":597,"line":598},[595,1569,1570],{},"UPDATE products\n",[595,1572,1573],{"class":597,"line":604},[595,1574,1575],{},"SET price = price * 1.1, updated_at = NOW()\n",[595,1577,1578],{"class":597,"line":610},[595,1579,1580],{},"WHERE category_id = 1;\n",[595,1582,1583],{"class":597,"line":616},[595,1584,885],{"emptyLinePlaceholder":884},[595,1586,1587],{"class":597,"line":622},[595,1588,1589],{},"-- UPDATE từ JOIN (Postgres dùng FROM)\n",[595,1591,1592],{"class":597,"line":628},[595,1593,1594],{},"UPDATE products p\n",[595,1596,1597],{"class":597,"line":869},[595,1598,1599],{},"SET category_name = c.name\n",[595,1601,1602],{"class":597,"line":875},[595,1603,1604],{},"FROM categories c\n",[595,1606,1607],{"class":597,"line":881},[595,1608,1609],{},"WHERE c.id = p.category_id;\n",[432,1611,1613],{"id":1612},"delete","DELETE",[586,1615,1617],{"className":588,"code":1616,"language":590,"meta":591,"style":591},"DELETE FROM products WHERE is_active = false;\n\n-- DELETE từ JOIN (USING)\nDELETE FROM orders o\nUSING users u\nWHERE u.id = o.user_id AND u.banned = true;\n",[467,1618,1619,1624,1628,1633,1638,1643],{"__ignoreMap":591},[595,1620,1621],{"class":597,"line":598},[595,1622,1623],{},"DELETE FROM products WHERE is_active = false;\n",[595,1625,1626],{"class":597,"line":604},[595,1627,885],{"emptyLinePlaceholder":884},[595,1629,1630],{"class":597,"line":610},[595,1631,1632],{},"-- DELETE từ JOIN (USING)\n",[595,1634,1635],{"class":597,"line":616},[595,1636,1637],{},"DELETE FROM orders o\n",[595,1639,1640],{"class":597,"line":622},[595,1641,1642],{},"USING users u\n",[595,1644,1645],{"class":597,"line":628},[595,1646,1647],{},"WHERE u.id = o.user_id AND u.banned = true;\n",[432,1649,1651],{"id":1650},"truncate","TRUNCATE",[586,1653,1655],{"className":588,"code":1654,"language":590,"meta":591,"style":591},"TRUNCATE TABLE logs;\nTRUNCATE TABLE logs RESTART IDENTITY;            -- reset sequence\nTRUNCATE TABLE logs RESTART IDENTITY CASCADE;    -- + truncate bảng FK reference\n",[467,1656,1657,1662,1667],{"__ignoreMap":591},[595,1658,1659],{"class":597,"line":598},[595,1660,1661],{},"TRUNCATE TABLE logs;\n",[595,1663,1664],{"class":597,"line":604},[595,1665,1666],{},"TRUNCATE TABLE logs RESTART IDENTITY;            -- reset sequence\n",[595,1668,1669],{"class":597,"line":610},[595,1670,1671],{},"TRUNCATE TABLE logs RESTART IDENTITY CASCADE;    -- + truncate bảng FK reference\n",[427,1673,1675],{"id":1674},"_4-null-handling","4. NULL handling",[586,1677,1679],{"className":588,"code":1678,"language":590,"meta":591,"style":591},"-- IS NULL \u002F IS NOT NULL\nSELECT * FROM users WHERE email IS NULL;\n\n-- COALESCE — first non-null\nSELECT name, COALESCE(phone, email, 'no-contact') FROM users;\n\n-- NULLIF — null nếu 2 giá trị bằng\nSELECT amount \u002F NULLIF(divisor, 0) FROM calc;\n",[467,1680,1681,1686,1691,1695,1700,1705,1709,1714],{"__ignoreMap":591},[595,1682,1683],{"class":597,"line":598},[595,1684,1685],{},"-- IS NULL \u002F IS NOT NULL\n",[595,1687,1688],{"class":597,"line":604},[595,1689,1690],{},"SELECT * FROM users WHERE email IS NULL;\n",[595,1692,1693],{"class":597,"line":610},[595,1694,885],{"emptyLinePlaceholder":884},[595,1696,1697],{"class":597,"line":616},[595,1698,1699],{},"-- COALESCE — first non-null\n",[595,1701,1702],{"class":597,"line":622},[595,1703,1704],{},"SELECT name, COALESCE(phone, email, 'no-contact') FROM users;\n",[595,1706,1707],{"class":597,"line":628},[595,1708,885],{"emptyLinePlaceholder":884},[595,1710,1711],{"class":597,"line":869},[595,1712,1713],{},"-- NULLIF — null nếu 2 giá trị bằng\n",[595,1715,1716],{"class":597,"line":875},[595,1717,1718],{},"SELECT amount \u002F NULLIF(divisor, 0) FROM calc;\n",[427,1720,1722],{"id":1721},"_5-function-thường-dùng","5. Function thường dùng",[432,1724,1726],{"id":1725},"string","String",[586,1728,1730],{"className":588,"code":1729,"language":590,"meta":591,"style":591},"SELECT\n    LENGTH('Hello'),                       -- 5\n    LOWER('ABC'), UPPER('abc'),\n    SUBSTRING('Hello' FROM 2 FOR 3),       -- 'ell'\n    LEFT('Hello', 3), RIGHT('Hello', 2),\n    TRIM('  abc  '),\n    REPLACE('abc', 'b', 'X'),\n    CONCAT('Hello', ' ', 'World'),\n    'Hello' || ' ' || 'World',             -- || operator\n    STRING_AGG(name, ', ' ORDER BY name);  -- aggregate\n",[467,1731,1732,1737,1742,1747,1752,1757,1762,1767,1772,1777],{"__ignoreMap":591},[595,1733,1734],{"class":597,"line":598},[595,1735,1736],{},"SELECT\n",[595,1738,1739],{"class":597,"line":604},[595,1740,1741],{},"    LENGTH('Hello'),                       -- 5\n",[595,1743,1744],{"class":597,"line":610},[595,1745,1746],{},"    LOWER('ABC'), UPPER('abc'),\n",[595,1748,1749],{"class":597,"line":616},[595,1750,1751],{},"    SUBSTRING('Hello' FROM 2 FOR 3),       -- 'ell'\n",[595,1753,1754],{"class":597,"line":622},[595,1755,1756],{},"    LEFT('Hello', 3), RIGHT('Hello', 2),\n",[595,1758,1759],{"class":597,"line":628},[595,1760,1761],{},"    TRIM('  abc  '),\n",[595,1763,1764],{"class":597,"line":869},[595,1765,1766],{},"    REPLACE('abc', 'b', 'X'),\n",[595,1768,1769],{"class":597,"line":875},[595,1770,1771],{},"    CONCAT('Hello', ' ', 'World'),\n",[595,1773,1774],{"class":597,"line":881},[595,1775,1776],{},"    'Hello' || ' ' || 'World',             -- || operator\n",[595,1778,1779],{"class":597,"line":888},[595,1780,1781],{},"    STRING_AGG(name, ', ' ORDER BY name);  -- aggregate\n",[432,1783,1785],{"id":1784},"date","Date",[586,1787,1789],{"className":588,"code":1788,"language":590,"meta":591,"style":591},"SELECT\n    NOW(),                                  -- TIMESTAMPTZ\n    CURRENT_DATE, CURRENT_TIME,\n    AGE(TIMESTAMP '1990-01-01'),            -- interval\n    NOW() + INTERVAL '7 days',\n    NOW() - INTERVAL '1 month',\n    EXTRACT(YEAR FROM NOW()),\n    DATE_TRUNC('month', NOW()),             -- truncate đến month\n    TO_CHAR(NOW(), 'YYYY-MM-DD HH24:MI');\n",[467,1790,1791,1795,1800,1805,1810,1815,1820,1825,1830],{"__ignoreMap":591},[595,1792,1793],{"class":597,"line":598},[595,1794,1736],{},[595,1796,1797],{"class":597,"line":604},[595,1798,1799],{},"    NOW(),                                  -- TIMESTAMPTZ\n",[595,1801,1802],{"class":597,"line":610},[595,1803,1804],{},"    CURRENT_DATE, CURRENT_TIME,\n",[595,1806,1807],{"class":597,"line":616},[595,1808,1809],{},"    AGE(TIMESTAMP '1990-01-01'),            -- interval\n",[595,1811,1812],{"class":597,"line":622},[595,1813,1814],{},"    NOW() + INTERVAL '7 days',\n",[595,1816,1817],{"class":597,"line":628},[595,1818,1819],{},"    NOW() - INTERVAL '1 month',\n",[595,1821,1822],{"class":597,"line":869},[595,1823,1824],{},"    EXTRACT(YEAR FROM NOW()),\n",[595,1826,1827],{"class":597,"line":875},[595,1828,1829],{},"    DATE_TRUNC('month', NOW()),             -- truncate đến month\n",[595,1831,1832],{"class":597,"line":881},[595,1833,1834],{},"    TO_CHAR(NOW(), 'YYYY-MM-DD HH24:MI');\n",[432,1836,1838],{"id":1837},"json-jsonb","JSON \u002F JSONB",[586,1840,1842],{"className":588,"code":1841,"language":590,"meta":591,"style":591},"-- Access\nSELECT\n    payload->'user' AS user_json,             -- trả JSONB\n    payload->>'user' AS user_text,            -- trả TEXT\n    payload->'tags'->0 AS first_tag,          -- index array\n    payload->'tags'->>0 AS first_tag_text;\n\n-- Existence\nSELECT * FROM events WHERE payload ? 'type';                  -- key tồn tại\nSELECT * FROM events WHERE payload ?| ARRAY['type', 'name'];  -- bất kỳ key\nSELECT * FROM events WHERE payload ?& ARRAY['type', 'name'];  -- tất cả key\n\n-- Containment (rất hay dùng + index GIN tốt)\nSELECT * FROM events WHERE payload @> '{\"type\": \"click\"}';\n\n-- Modify (18+ có thêm nhiều function)\nUPDATE events\nSET payload = jsonb_set(payload, '{processed}', 'true')\nWHERE id = 1;\n\n-- Path\nSELECT jsonb_path_query(payload, '$.tags[*]') FROM events;\n",[467,1843,1844,1849,1853,1858,1863,1868,1873,1877,1882,1887,1892,1897,1901,1906,1911,1915,1920,1925,1930,1935,1939,1944],{"__ignoreMap":591},[595,1845,1846],{"class":597,"line":598},[595,1847,1848],{},"-- Access\n",[595,1850,1851],{"class":597,"line":604},[595,1852,1736],{},[595,1854,1855],{"class":597,"line":610},[595,1856,1857],{},"    payload->'user' AS user_json,             -- trả JSONB\n",[595,1859,1860],{"class":597,"line":616},[595,1861,1862],{},"    payload->>'user' AS user_text,            -- trả TEXT\n",[595,1864,1865],{"class":597,"line":622},[595,1866,1867],{},"    payload->'tags'->0 AS first_tag,          -- index array\n",[595,1869,1870],{"class":597,"line":628},[595,1871,1872],{},"    payload->'tags'->>0 AS first_tag_text;\n",[595,1874,1875],{"class":597,"line":869},[595,1876,885],{"emptyLinePlaceholder":884},[595,1878,1879],{"class":597,"line":875},[595,1880,1881],{},"-- Existence\n",[595,1883,1884],{"class":597,"line":881},[595,1885,1886],{},"SELECT * FROM events WHERE payload ? 'type';                  -- key tồn tại\n",[595,1888,1889],{"class":597,"line":888},[595,1890,1891],{},"SELECT * FROM events WHERE payload ?| ARRAY['type', 'name'];  -- bất kỳ key\n",[595,1893,1894],{"class":597,"line":894},[595,1895,1896],{},"SELECT * FROM events WHERE payload ?& ARRAY['type', 'name'];  -- tất cả key\n",[595,1898,1899],{"class":597,"line":900},[595,1900,885],{"emptyLinePlaceholder":884},[595,1902,1903],{"class":597,"line":905},[595,1904,1905],{},"-- Containment (rất hay dùng + index GIN tốt)\n",[595,1907,1908],{"class":597,"line":911},[595,1909,1910],{},"SELECT * FROM events WHERE payload @> '{\"type\": \"click\"}';\n",[595,1912,1913],{"class":597,"line":916},[595,1914,885],{"emptyLinePlaceholder":884},[595,1916,1917],{"class":597,"line":922},[595,1918,1919],{},"-- Modify (18+ có thêm nhiều function)\n",[595,1921,1922],{"class":597,"line":928},[595,1923,1924],{},"UPDATE events\n",[595,1926,1927],{"class":597,"line":933},[595,1928,1929],{},"SET payload = jsonb_set(payload, '{processed}', 'true')\n",[595,1931,1932],{"class":597,"line":939},[595,1933,1934],{},"WHERE id = 1;\n",[595,1936,1937],{"class":597,"line":945},[595,1938,885],{"emptyLinePlaceholder":884},[595,1940,1941],{"class":597,"line":951},[595,1942,1943],{},"-- Path\n",[595,1945,1946],{"class":597,"line":956},[595,1947,1948],{},"SELECT jsonb_path_query(payload, '$.tags[*]') FROM events;\n",[688,1950,1951,1957],{"color":690,"icon":691},[693,1952,695,1953,1956],{},[534,1954,1955],{},"Index GIN trên JSONB"," — siêu mạnh:",[586,1958,1960],{"className":588,"code":1959,"language":590,"meta":591,"style":591},"CREATE INDEX idx_events_payload ON events USING GIN (payload);\n-- Hoặc cho jsonb_path_ops nếu chỉ dùng @> operator (nhỏ hơn, nhanh hơn)\nCREATE INDEX idx_events_payload ON events USING GIN (payload jsonb_path_ops);\n",[467,1961,1962,1967,1972],{"__ignoreMap":591},[595,1963,1964],{"class":597,"line":598},[595,1965,1966],{},"CREATE INDEX idx_events_payload ON events USING GIN (payload);\n",[595,1968,1969],{"class":597,"line":604},[595,1970,1971],{},"-- Hoặc cho jsonb_path_ops nếu chỉ dùng @> operator (nhỏ hơn, nhanh hơn)\n",[595,1973,1974],{"class":597,"line":610},[595,1975,1976],{},"CREATE INDEX idx_events_payload ON events USING GIN (payload jsonb_path_ops);\n",[432,1978,1980],{"id":1979},"array","Array",[586,1982,1984],{"className":588,"code":1983,"language":590,"meta":591,"style":591},"SELECT\n    ARRAY[1, 2, 3, 4],\n    ARRAY_LENGTH(ARRAY[1, 2, 3], 1),       -- 3\n    ARRAY[1, 2] || ARRAY[3, 4],            -- {1,2,3,4}\n    1 = ANY(ARRAY[1, 2, 3]),               -- TRUE\n    ARRAY_AGG(name ORDER BY name);\n\n-- Array trong WHERE\nSELECT * FROM posts WHERE tags && ARRAY['vue', 'react'];   -- overlap\nSELECT * FROM posts WHERE tags @> ARRAY['vue'];            -- contains\n",[467,1985,1986,1990,1995,2000,2005,2010,2015,2019,2024,2029],{"__ignoreMap":591},[595,1987,1988],{"class":597,"line":598},[595,1989,1736],{},[595,1991,1992],{"class":597,"line":604},[595,1993,1994],{},"    ARRAY[1, 2, 3, 4],\n",[595,1996,1997],{"class":597,"line":610},[595,1998,1999],{},"    ARRAY_LENGTH(ARRAY[1, 2, 3], 1),       -- 3\n",[595,2001,2002],{"class":597,"line":616},[595,2003,2004],{},"    ARRAY[1, 2] || ARRAY[3, 4],            -- {1,2,3,4}\n",[595,2006,2007],{"class":597,"line":622},[595,2008,2009],{},"    1 = ANY(ARRAY[1, 2, 3]),               -- TRUE\n",[595,2011,2012],{"class":597,"line":628},[595,2013,2014],{},"    ARRAY_AGG(name ORDER BY name);\n",[595,2016,2017],{"class":597,"line":869},[595,2018,885],{"emptyLinePlaceholder":884},[595,2020,2021],{"class":597,"line":875},[595,2022,2023],{},"-- Array trong WHERE\n",[595,2025,2026],{"class":597,"line":881},[595,2027,2028],{},"SELECT * FROM posts WHERE tags && ARRAY['vue', 'react'];   -- overlap\n",[595,2030,2031],{"class":597,"line":888},[595,2032,2033],{},"SELECT * FROM posts WHERE tags @> ARRAY['vue'];            -- contains\n",[427,2035,2037],{"id":2036},"_6-variable-block-plpgsql","6. Variable & block (PL\u002FpgSQL)",[693,2039,2040,2041,2044],{},"Postgres dùng ",[534,2042,2043],{},"PL\u002FpgSQL"," thay vì T-SQL. Cú pháp khác hẳn:",[586,2046,2048],{"className":588,"code":2047,"language":590,"meta":591,"style":591},"DO $$\nDECLARE\n    user_count INT;\nBEGIN\n    SELECT COUNT(*) INTO user_count FROM users;\n    RAISE NOTICE 'Total users: %', user_count;\nEND $$;\n",[467,2049,2050,2055,2060,2065,2070,2075,2080],{"__ignoreMap":591},[595,2051,2052],{"class":597,"line":598},[595,2053,2054],{},"DO $$\n",[595,2056,2057],{"class":597,"line":604},[595,2058,2059],{},"DECLARE\n",[595,2061,2062],{"class":597,"line":610},[595,2063,2064],{},"    user_count INT;\n",[595,2066,2067],{"class":597,"line":616},[595,2068,2069],{},"BEGIN\n",[595,2071,2072],{"class":597,"line":622},[595,2073,2074],{},"    SELECT COUNT(*) INTO user_count FROM users;\n",[595,2076,2077],{"class":597,"line":628},[595,2078,2079],{},"    RAISE NOTICE 'Total users: %', user_count;\n",[595,2081,2082],{"class":597,"line":869},[595,2083,2084],{},"END $$;\n",[693,2086,2087,2090,2091,2094,2095,686],{},[467,2088,2089],{},"$$ ... $$"," là ",[534,2092,2093],{},"dollar quoting"," — string không cần escape, có thể chứa ",[467,2096,2097],{},"'",[427,2099,2101],{"id":2100},"_7-function-stored-procedure","7. Function & Stored Procedure",[432,2103,2105],{"id":2104},"function-return-value","Function (return value)",[586,2107,2109],{"className":588,"code":2108,"language":590,"meta":591,"style":591},"CREATE OR REPLACE FUNCTION full_name(first TEXT, last TEXT)\nRETURNS TEXT\nLANGUAGE SQL IMMUTABLE\nAS $$\n    SELECT first || ' ' || last;\n$$;\n\nSELECT full_name('Alice', 'Nguyen');\n",[467,2110,2111,2116,2121,2126,2131,2136,2141,2145],{"__ignoreMap":591},[595,2112,2113],{"class":597,"line":598},[595,2114,2115],{},"CREATE OR REPLACE FUNCTION full_name(first TEXT, last TEXT)\n",[595,2117,2118],{"class":597,"line":604},[595,2119,2120],{},"RETURNS TEXT\n",[595,2122,2123],{"class":597,"line":610},[595,2124,2125],{},"LANGUAGE SQL IMMUTABLE\n",[595,2127,2128],{"class":597,"line":616},[595,2129,2130],{},"AS $$\n",[595,2132,2133],{"class":597,"line":622},[595,2134,2135],{},"    SELECT first || ' ' || last;\n",[595,2137,2138],{"class":597,"line":628},[595,2139,2140],{},"$$;\n",[595,2142,2143],{"class":597,"line":869},[595,2144,885],{"emptyLinePlaceholder":884},[595,2146,2147],{"class":597,"line":875},[595,2148,2149],{},"SELECT full_name('Alice', 'Nguyen');\n",[432,2151,2153],{"id":2152},"plpgsql-function-với-logic","PL\u002FpgSQL function với logic",[586,2155,2157],{"className":588,"code":2156,"language":590,"meta":591,"style":591},"CREATE OR REPLACE FUNCTION get_user_tier(uid BIGINT)\nRETURNS TEXT\nLANGUAGE plpgsql\nAS $$\nDECLARE\n    spent NUMERIC;\nBEGIN\n    SELECT COALESCE(SUM(total), 0) INTO spent\n    FROM orders WHERE user_id = uid;\n    \n    IF spent >= 100000000 THEN RETURN 'gold';\n    ELSIF spent >= 10000000 THEN RETURN 'silver';\n    ELSE RETURN 'bronze';\n    END IF;\nEND $$;\n",[467,2158,2159,2164,2168,2173,2177,2181,2186,2190,2195,2200,2205,2210,2215,2220,2225],{"__ignoreMap":591},[595,2160,2161],{"class":597,"line":598},[595,2162,2163],{},"CREATE OR REPLACE FUNCTION get_user_tier(uid BIGINT)\n",[595,2165,2166],{"class":597,"line":604},[595,2167,2120],{},[595,2169,2170],{"class":597,"line":610},[595,2171,2172],{},"LANGUAGE plpgsql\n",[595,2174,2175],{"class":597,"line":616},[595,2176,2130],{},[595,2178,2179],{"class":597,"line":622},[595,2180,2059],{},[595,2182,2183],{"class":597,"line":628},[595,2184,2185],{},"    spent NUMERIC;\n",[595,2187,2188],{"class":597,"line":869},[595,2189,2069],{},[595,2191,2192],{"class":597,"line":875},[595,2193,2194],{},"    SELECT COALESCE(SUM(total), 0) INTO spent\n",[595,2196,2197],{"class":597,"line":881},[595,2198,2199],{},"    FROM orders WHERE user_id = uid;\n",[595,2201,2202],{"class":597,"line":888},[595,2203,2204],{},"    \n",[595,2206,2207],{"class":597,"line":894},[595,2208,2209],{},"    IF spent >= 100000000 THEN RETURN 'gold';\n",[595,2211,2212],{"class":597,"line":900},[595,2213,2214],{},"    ELSIF spent >= 10000000 THEN RETURN 'silver';\n",[595,2216,2217],{"class":597,"line":905},[595,2218,2219],{},"    ELSE RETURN 'bronze';\n",[595,2221,2222],{"class":597,"line":911},[595,2223,2224],{},"    END IF;\n",[595,2226,2227],{"class":597,"line":916},[595,2228,2084],{},[432,2230,2232],{"id":2231},"procedure-11-có-thể-commit-trong-thân-hàm","Procedure (11+, có thể COMMIT trong thân hàm)",[586,2234,2236],{"className":588,"code":2235,"language":590,"meta":591,"style":591},"CREATE OR REPLACE PROCEDURE transfer(from_id BIGINT, to_id BIGINT, amount NUMERIC)\nLANGUAGE plpgsql\nAS $$\nBEGIN\n    UPDATE accounts SET balance = balance - amount WHERE id = from_id;\n    UPDATE accounts SET balance = balance + amount WHERE id = to_id;\n    COMMIT;\nEND $$;\n\nCALL transfer(1, 2, 1000000);\n",[467,2237,2238,2243,2247,2251,2255,2260,2265,2270,2274,2278],{"__ignoreMap":591},[595,2239,2240],{"class":597,"line":598},[595,2241,2242],{},"CREATE OR REPLACE PROCEDURE transfer(from_id BIGINT, to_id BIGINT, amount NUMERIC)\n",[595,2244,2245],{"class":597,"line":604},[595,2246,2172],{},[595,2248,2249],{"class":597,"line":610},[595,2250,2130],{},[595,2252,2253],{"class":597,"line":616},[595,2254,2069],{},[595,2256,2257],{"class":597,"line":622},[595,2258,2259],{},"    UPDATE accounts SET balance = balance - amount WHERE id = from_id;\n",[595,2261,2262],{"class":597,"line":628},[595,2263,2264],{},"    UPDATE accounts SET balance = balance + amount WHERE id = to_id;\n",[595,2266,2267],{"class":597,"line":869},[595,2268,2269],{},"    COMMIT;\n",[595,2271,2272],{"class":597,"line":875},[595,2273,2084],{},[595,2275,2276],{"class":597,"line":881},[595,2277,885],{"emptyLinePlaceholder":884},[595,2279,2280],{"class":597,"line":888},[595,2281,2282],{},"CALL transfer(1, 2, 1000000);\n",[688,2284,2286,2291],{"color":804,"icon":2285},"i-lucide-info",[693,2287,2288],{},[534,2289,2290],{},"Function vs Procedure trong Postgres:",[2292,2293,2294,2298],"ul",{},[2295,2296,2297],"li",{},"Function: return value, không COMMIT\u002FROLLBACK trong thân.",[2295,2299,2300,2301,2304,2305,686],{},"Procedure (Postgres 11+): không return value, ",[534,2302,2303],{},"có thể COMMIT\u002FROLLBACK"," trong thân, gọi bằng ",[467,2306,2307],{},"CALL",[427,2309,2311],{"id":2310},"_8-cải-tiến-dml-trong-postgresql-19","8. Cải tiến DML trong PostgreSQL 19",[693,2313,2314,2315,2318],{},"PostgreSQL 19 (ra mắt 2026) mang đến ba cải tiến DML quan trọng giúp code ngắn hơn, nhanh hơn và chuẩn SQL hơn. Nếu đã quen thuộc với PG 16-18, đây là những tính năng bạn ",[534,2316,2317],{},"phải biết"," cho năm 2026.",[432,2320,2322,2323,2325],{"id":2321},"_81-on-conflict-do-select-atomic-get-or-create","8.1 ",[467,2324,1546],{}," — Atomic get-or-create",[693,2327,2328,2329,716,2332,2335,2336,2338],{},"Đây là hành động thứ ba sau ",[467,2330,2331],{},"DO NOTHING",[467,2333,2334],{},"DO UPDATE",", giúp thực hiện ",[534,2337,1550],{}," chỉ trong một câu lệnh duy nhất — không race condition, không dead tuple.",[586,2340,2342],{"className":588,"code":2341,"language":590,"meta":591,"style":591},"-- PG 19 — lấy row nếu tồn tại, insert nếu chưa, luôn trả về kết quả\nINSERT INTO users (email, name)\nVALUES ('alice@example.com', 'Alice')\nON CONFLICT (email) DO SELECT\nRETURNING *;\n",[467,2343,2344,2349,2354,2359,2364],{"__ignoreMap":591},[595,2345,2346],{"class":597,"line":598},[595,2347,2348],{},"-- PG 19 — lấy row nếu tồn tại, insert nếu chưa, luôn trả về kết quả\n",[595,2350,2351],{"class":597,"line":604},[595,2352,2353],{},"INSERT INTO users (email, name)\n",[595,2355,2356],{"class":597,"line":610},[595,2357,2358],{},"VALUES ('alice@example.com', 'Alice')\n",[595,2360,2361],{"class":597,"line":616},[595,2362,2363],{},"ON CONFLICT (email) DO SELECT\n",[595,2365,2366],{"class":597,"line":622},[595,2367,2368],{},"RETURNING *;\n",[693,2370,2371],{},[534,2372,2373],{},"So sánh các pattern get-or-create:",[437,2375,2376,2389],{},[440,2377,2378],{},[443,2379,2380,2383,2386],{},[446,2381,2382],{},"Cách",[446,2384,2385],{},"Ưu điểm",[446,2387,2388],{},"Nhược điểm",[459,2390,2391,2404,2422],{},[443,2392,2393,2398,2401],{},[464,2394,2395,2397],{},[467,2396,2331],{}," + SELECT riêng",[464,2399,2400],{},"Đơn giản",[464,2402,2403],{},"Cần 2 câu lệnh, 2 round-trip",[443,2405,2406,2412,2415],{},[464,2407,2408,2411],{},[467,2409,2410],{},"DO UPDATE SET col = EXCLUDED.col"," (no-op update)",[464,2413,2414],{},"1 câu lệnh",[464,2416,2417,2418,2421],{},"Tạo ",[534,2419,2420],{},"dead tuple",", bloat, chậm vacuum",[443,2423,2424,2430,2436],{},[464,2425,2426,2429],{},[467,2427,2428],{},"DO SELECT"," (PG 19)",[464,2431,2432,2433],{},"1 câu lệnh, atomic, ",[534,2434,2435],{},"không dead tuple",[464,2437,2438],{},"Cần PG 19+",[586,2440,2442],{"className":588,"code":2441,"language":590,"meta":591,"style":591},"-- Pattern cũ (PG 18 trở về trước) — vẫn chạy nhưng tạo dead tuple không cần thiết\nINSERT INTO users (email, name)\nVALUES ('alice@example.com', 'Alice')\nON CONFLICT (email) DO UPDATE SET email = EXCLUDED.email  -- dirty no-op\nRETURNING *;\n\n-- PG 19 — sạch hơn, nhanh hơn ~4x\nINSERT INTO users (email, name)\nVALUES ('alice@example.com', 'Alice')\nON CONFLICT (email) DO SELECT\nRETURNING *;\n",[467,2443,2444,2449,2453,2457,2462,2466,2470,2475,2479,2483,2487],{"__ignoreMap":591},[595,2445,2446],{"class":597,"line":598},[595,2447,2448],{},"-- Pattern cũ (PG 18 trở về trước) — vẫn chạy nhưng tạo dead tuple không cần thiết\n",[595,2450,2451],{"class":597,"line":604},[595,2452,2353],{},[595,2454,2455],{"class":597,"line":610},[595,2456,2358],{},[595,2458,2459],{"class":597,"line":616},[595,2460,2461],{},"ON CONFLICT (email) DO UPDATE SET email = EXCLUDED.email  -- dirty no-op\n",[595,2463,2464],{"class":597,"line":622},[595,2465,2368],{},[595,2467,2468],{"class":597,"line":628},[595,2469,885],{"emptyLinePlaceholder":884},[595,2471,2472],{"class":597,"line":869},[595,2473,2474],{},"-- PG 19 — sạch hơn, nhanh hơn ~4x\n",[595,2476,2477],{"class":597,"line":875},[595,2478,2353],{},[595,2480,2481],{"class":597,"line":881},[595,2482,2358],{},[595,2484,2485],{"class":597,"line":888},[595,2486,2363],{},[595,2488,2489],{"class":597,"line":894},[595,2490,2368],{},[688,2492,2493,2501],{"color":690,"icon":691},[693,2494,695,2495,699,2498],{},[534,2496,2497],{},"Câu PV 2026:",[701,2499,2500],{},"\"ON CONFLICT DO SELECT khác gì DO NOTHING?\"",[705,2502,2503],{},[693,2504,2505,2506,2508,2509,2512,2513,2515,2516,2519,2520,2522],{},"\"",[467,2507,2331],{}," chỉ insert nếu không conflict — nếu conflict thì ",[534,2510,2511],{},"không return gì cả",", bạn buộc phải SELECT lại để lấy row đã tồn tại. ",[467,2514,2428],{}," trả về luôn row bị conflict, giúp bạn có kết quả sau ",[534,2517,2518],{},"đúng 1 câu lệnh",". Đây là pattern ",[534,2521,1550],{}," chuẩn chỉ: không race condition (không ai xen giữa SELECT và INSERT được), không dead tuple (không phải UPDATE vô nghĩa), và nhanh hơn ~4x so với cách no-op update cũ.\"",[688,2524,2525,2530],{"color":804,"icon":2285},[693,2526,2527],{},[534,2528,2529],{},"Khi nào dùng gì:",[2292,2531,2532,2537,2547],{},[2295,2533,2534,2536],{},[467,2535,2331],{}," — khi bạn không cần row trả về (VD: log, dedup silent)",[2295,2538,2539,2542,2543,2546],{},[467,2540,2541],{},"DO UPDATE SET"," — khi bạn ",[534,2544,2545],{},"thực sự muốn update"," dữ liệu (VD: counter, merge)",[2295,2548,2549,2551,2552,2555],{},[467,2550,2428],{}," — khi bạn muốn ",[534,2553,2554],{},"lấy row về"," mà không cần update gì (VD: ensure tồn tại rồi đọc)",[432,2557,2559,2560,2563],{"id":2558},"_82-group-by-all-nhóm-theo-mọi-cột-non-aggregate","8.2 ",[467,2561,2562],{},"GROUP BY ALL"," — Nhóm theo mọi cột non-aggregate",[688,2565,2566,2569],{"color":804,"icon":2285},[534,2567,2568],{},"Có trong:"," PostgreSQL 19",[586,2571,2573],{"className":588,"code":2572,"language":590,"meta":591,"style":591},"-- PG 19 — tự động GROUP BY mọi non-aggregate column trong SELECT\nSELECT department, role, count(*)\nFROM employees\nGROUP BY ALL;\n",[467,2574,2575,2580,2585,2590],{"__ignoreMap":591},[595,2576,2577],{"class":597,"line":598},[595,2578,2579],{},"-- PG 19 — tự động GROUP BY mọi non-aggregate column trong SELECT\n",[595,2581,2582],{"class":597,"line":604},[595,2583,2584],{},"SELECT department, role, count(*)\n",[595,2586,2587],{"class":597,"line":610},[595,2588,2589],{},"FROM employees\n",[595,2591,2592],{"class":597,"line":616},[595,2593,2594],{},"GROUP BY ALL;\n",[693,2596,2597,2598,2601,2602,2605,2606,2608,2609,686],{},"Không cần liệt kê từng cột trong ",[467,2599,2600],{},"GROUP BY"," nữa. Khi thêm hoặc xóa cột trong ",[467,2603,2604],{},"SELECT",", ",[467,2607,2562],{}," tự cập nhật — ",[534,2610,2611],{},"không còn lỗi quên sửa GROUP BY",[586,2613,2615],{"className":588,"code":2614,"language":590,"meta":591,"style":591},"-- Thêm cột location mà không cần sửa GROUP BY\nSELECT department, role, location, count(*)\nFROM employees\nGROUP BY ALL;  -- tự hiểu là GROUP BY department, role, location\n\n-- Xóa cột cũng vậy — GROUP BY ALL tự điều chỉnh\nSELECT department, count(*)\nFROM employees\nGROUP BY ALL;  -- tự hiểu là GROUP BY department\n",[467,2616,2617,2622,2627,2631,2636,2640,2645,2650,2654],{"__ignoreMap":591},[595,2618,2619],{"class":597,"line":598},[595,2620,2621],{},"-- Thêm cột location mà không cần sửa GROUP BY\n",[595,2623,2624],{"class":597,"line":604},[595,2625,2626],{},"SELECT department, role, location, count(*)\n",[595,2628,2629],{"class":597,"line":610},[595,2630,2589],{},[595,2632,2633],{"class":597,"line":616},[595,2634,2635],{},"GROUP BY ALL;  -- tự hiểu là GROUP BY department, role, location\n",[595,2637,2638],{"class":597,"line":622},[595,2639,885],{"emptyLinePlaceholder":884},[595,2641,2642],{"class":597,"line":628},[595,2643,2644],{},"-- Xóa cột cũng vậy — GROUP BY ALL tự điều chỉnh\n",[595,2646,2647],{"class":597,"line":869},[595,2648,2649],{},"SELECT department, count(*)\n",[595,2651,2652],{"class":597,"line":875},[595,2653,2589],{},[595,2655,2656],{"class":597,"line":881},[595,2657,2658],{},"GROUP BY ALL;  -- tự hiểu là GROUP BY department\n",[688,2660,2661,2668],{"color":690,"icon":691},[693,2662,695,2663,699,2665],{},[534,2664,1524],{},[701,2666,2667],{},"\"GROUP BY ALL có vấn đề gì về hiệu năng không?\"",[705,2669,2670],{},[693,2671,2672,2673,2675,2676,2679,2680,2683],{},"\"Không. ",[467,2674,2562],{}," được planner expand ra danh sách cột tường minh tại ",[534,2677,2678],{},"parse time",", kế hoạch thực thi giống hệt như viết tay. Nó là ",[534,2681,2682],{},"syntactic sugar"," — tiện lợi mà không phải trả giá. Lưu ý duy nhất: nếu bạn có column trong SELECT là aggregate expression lồng ghép, hãy kiểm tra lại xem planner có hiểu đúng ý bạn không.\"",[432,2685,2687,2688,2691],{"id":2686},"_83-for-portion-of-cập-nhật-dữ-liệu-temporal","8.3 ",[467,2689,2690],{},"FOR PORTION OF"," — Cập nhật dữ liệu temporal",[688,2693,2694,2696],{"color":804,"icon":2285},[534,2695,2568],{}," PostgreSQL 19 — hoàn thiện bộ tính năng temporal SQL:2011",[693,2698,2699,2700,2703,2704,2706,2707,2710],{},"Khi làm việc với ",[534,2701,2702],{},"dữ liệu lịch sử"," (slowly changing dimension, bảng giá theo thời gian, lương nhân viên qua các kỳ), ",[467,2705,2690],{}," tự động ",[534,2708,2709],{},"chia row"," để chỉ cập nhật đúng khoảng thời gian cần thiết:",[586,2712,2714],{"className":588,"code":2713,"language":590,"meta":591,"style":591},"-- Bảng giá sản phẩm theo thời gian (temporal table)\nCREATE TABLE product_prices (\n    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n    product_id INT NOT NULL,\n    price NUMERIC(18, 2) NOT NULL,\n    valid_range TSRANGE NOT NULL,\n    EXCLUDE USING GIST (product_id WITH =, valid_range WITH &&)\n);\n\n-- Dữ liệu ban đầu: giá 29.99 áp dụng cả năm 2025\nINSERT INTO product_prices (product_id, price, valid_range)\nVALUES (1, 29.99, '[2025-01-01, 2025-12-31)');\n\n-- PG 19 — cập nhật giá cho Q3\u002F2025, Postgres tự chia row\nUPDATE product_prices\nFOR PORTION OF valid_range FROM '2025-07-01' TO '2025-10-01'\nSET price = 34.99\nWHERE product_id = 1;\n",[467,2715,2716,2721,2726,2730,2735,2740,2745,2750,2754,2758,2763,2768,2773,2777,2782,2787,2792,2797],{"__ignoreMap":591},[595,2717,2718],{"class":597,"line":598},[595,2719,2720],{},"-- Bảng giá sản phẩm theo thời gian (temporal table)\n",[595,2722,2723],{"class":597,"line":604},[595,2724,2725],{},"CREATE TABLE product_prices (\n",[595,2727,2728],{"class":597,"line":610},[595,2729,613],{},[595,2731,2732],{"class":597,"line":616},[595,2733,2734],{},"    product_id INT NOT NULL,\n",[595,2736,2737],{"class":597,"line":622},[595,2738,2739],{},"    price NUMERIC(18, 2) NOT NULL,\n",[595,2741,2742],{"class":597,"line":628},[595,2743,2744],{},"    valid_range TSRANGE NOT NULL,\n",[595,2746,2747],{"class":597,"line":869},[595,2748,2749],{},"    EXCLUDE USING GIST (product_id WITH =, valid_range WITH &&)\n",[595,2751,2752],{"class":597,"line":875},[595,2753,631],{},[595,2755,2756],{"class":597,"line":881},[595,2757,885],{"emptyLinePlaceholder":884},[595,2759,2760],{"class":597,"line":888},[595,2761,2762],{},"-- Dữ liệu ban đầu: giá 29.99 áp dụng cả năm 2025\n",[595,2764,2765],{"class":597,"line":894},[595,2766,2767],{},"INSERT INTO product_prices (product_id, price, valid_range)\n",[595,2769,2770],{"class":597,"line":900},[595,2771,2772],{},"VALUES (1, 29.99, '[2025-01-01, 2025-12-31)');\n",[595,2774,2775],{"class":597,"line":905},[595,2776,885],{"emptyLinePlaceholder":884},[595,2778,2779],{"class":597,"line":911},[595,2780,2781],{},"-- PG 19 — cập nhật giá cho Q3\u002F2025, Postgres tự chia row\n",[595,2783,2784],{"class":597,"line":916},[595,2785,2786],{},"UPDATE product_prices\n",[595,2788,2789],{"class":597,"line":922},[595,2790,2791],{},"FOR PORTION OF valid_range FROM '2025-07-01' TO '2025-10-01'\n",[595,2793,2794],{"class":597,"line":928},[595,2795,2796],{},"SET price = 34.99\n",[595,2798,2799],{"class":597,"line":933},[595,2800,2801],{},"WHERE product_id = 1;\n",[693,2803,2804,2807,2808,2811],{},[534,2805,2806],{},"Postgres tự động làm gì?"," Row gốc ",[467,2809,2810],{},"valid_range = [2025-01-01, 2025-12-31)"," được tự động chia thành 3 row:",[2292,2813,2814,2820,2826],{},[2295,2815,2816,2819],{},[467,2817,2818],{},"[2025-01-01, 2025-07-01)"," — giá cũ 29.99",[2295,2821,2822,2825],{},[467,2823,2824],{},"[2025-07-01, 2025-10-01)"," — giá mới 34.99",[2295,2827,2828,2819],{},[467,2829,2830],{},"[2025-10-01, 2025-12-31)",[693,2832,2833,2836],{},[534,2834,2835],{},"Không cần viết 3-4 câu INSERT\u002FDELETE\u002FUPDATE thủ công"," như trước PG 19.",[688,2838,2839,2846],{"color":690,"icon":691},[693,2840,695,2841,699,2843],{},[534,2842,1524],{},[701,2844,2845],{},"\"FOR PORTION OF dùng khi nào? So sánh với cách thủ công?\"",[705,2847,2848],{},[693,2849,2850,2851,2854,2855,2857,2858,2861,2862,2865],{},"\"Dùng khi bạn có ",[534,2852,2853],{},"dữ liệu phiên bản theo thời gian"," (temporal\u002Fbitemporal tables) — bảng giá sản phẩm, lương nhân viên, trạng thái hợp đồng. Trước PG 19, để update một khoảng thời gian bạn phải: (1) SELECT row cũ, (2) UPDATE row cũ thu hẹp range, (3) INSERT row mới cho khoảng update, (4) INSERT row mới cho phần còn lại. ",[467,2856,2690],{}," làm tất cả trong 1 câu — ",[534,2859,2860],{},"tránh race condition"," và code đơn giản hơn nhiều. Đây là chuẩn ",[534,2863,2864],{},"SQL:2011 temporal"," — trước PG 19 chỉ có DB2 và Oracle hỗ trợ đầy đủ.\"",[432,2867,2869,2870,2873],{"id":2868},"_84-ignore-nulls-cho-window-functions","8.4 ",[467,2871,2872],{},"IGNORE NULLS"," cho Window Functions",[688,2875,2876,2569],{"color":804,"icon":2285},[534,2877,2568],{},[693,2879,2880,2881,2605,2884,2605,2887,2605,2890,2605,2893,2896,2897,2899],{},"Các window function ",[467,2882,2883],{},"lead()",[467,2885,2886],{},"lag()",[467,2888,2889],{},"first_value()",[467,2891,2892],{},"last_value()",[467,2894,2895],{},"nth_value()"," giờ hỗ trợ ",[467,2898,2872],{}," để bỏ qua giá trị NULL khi duyệt qua partition:",[586,2901,2903],{"className":588,"code":2902,"language":590,"meta":591,"style":591},"-- PG 19 — forward-fill: điền giá trị sensor gần nhất không NULL\nSELECT\n    sensor_id,\n    ts,\n    reading,\n    last_value(reading) IGNORE NULLS OVER (\n        PARTITION BY sensor_id ORDER BY ts\n    ) AS last_known_reading\nFROM sensor_data;\n",[467,2904,2905,2910,2914,2919,2924,2929,2934,2939,2944],{"__ignoreMap":591},[595,2906,2907],{"class":597,"line":598},[595,2908,2909],{},"-- PG 19 — forward-fill: điền giá trị sensor gần nhất không NULL\n",[595,2911,2912],{"class":597,"line":604},[595,2913,1736],{},[595,2915,2916],{"class":597,"line":610},[595,2917,2918],{},"    sensor_id,\n",[595,2920,2921],{"class":597,"line":616},[595,2922,2923],{},"    ts,\n",[595,2925,2926],{"class":597,"line":622},[595,2927,2928],{},"    reading,\n",[595,2930,2931],{"class":597,"line":628},[595,2932,2933],{},"    last_value(reading) IGNORE NULLS OVER (\n",[595,2935,2936],{"class":597,"line":869},[595,2937,2938],{},"        PARTITION BY sensor_id ORDER BY ts\n",[595,2940,2941],{"class":597,"line":875},[595,2942,2943],{},"    ) AS last_known_reading\n",[595,2945,2946],{"class":597,"line":881},[595,2947,2948],{},"FROM sensor_data;\n",[693,2950,2951],{},[534,2952,2953],{},"So sánh hành vi:",[437,2955,2956,2972],{},[440,2957,2958],{},[443,2959,2960,2962,2968],{},[446,2961],{},[446,2963,2964,2967],{},[467,2965,2966],{},"RESPECT NULLS"," (mặc định)",[446,2969,2970],{},[467,2971,2872],{},[459,2973,2974,2985],{},[443,2975,2976,2979,2982],{},[464,2977,2978],{},"Gặp NULL",[464,2980,2981],{},"Trả về NULL",[464,2983,2984],{},"Bỏ qua, lấy giá trị non-NULL gần nhất",[443,2986,2987,2990,2993],{},[464,2988,2989],{},"Use case",[464,2991,2992],{},"Giữ nguyên dữ liệu gốc",[464,2994,2995],{},"Forward-fill, backward-fill, gap-filling",[688,2997,2998,2999,3001,3002,686],{"color":804,"icon":2285},"Mặc định là ",[467,3000,2966],{}," (giữ nguyên NULL — tương đương hành vi cũ). Chi tiết về window functions xem tại bài ",[1553,3003,3004],{"href":210},"Truy vấn nâng cao",[3006,3007],"hr",{},[688,3009,3011,3012],{"color":804,"icon":3010},"i-lucide-arrow-right","Tiếp theo: ",[1553,3013,3014],{"href":210},"Truy vấn nâng cao Postgres — JOIN, CTE, window, MVCC",[3016,3017,3018],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":591,"searchDepth":604,"depth":604,"links":3020},[3021,3028,3031,3040,3041,3047,3048,3053],{"id":429,"depth":604,"text":430,"children":3022},[3023,3024,3025,3026,3027],{"id":434,"depth":610,"text":435},{"id":634,"depth":610,"text":635},{"id":732,"depth":610,"text":733},{"id":831,"depth":610,"text":832},{"id":1084,"depth":610,"text":1085},{"id":1163,"depth":604,"text":1164,"children":3029},[3030],{"id":1285,"depth":610,"text":1286},{"id":1347,"depth":604,"text":1348,"children":3032},[3033,3034,3035,3037,3038,3039],{"id":1351,"depth":610,"text":1352},{"id":1428,"depth":610,"text":1429},{"id":1457,"depth":610,"text":3036},"UPSERT — ON CONFLICT",{"id":1559,"depth":610,"text":1560},{"id":1612,"depth":610,"text":1613},{"id":1650,"depth":610,"text":1651},{"id":1674,"depth":604,"text":1675},{"id":1721,"depth":604,"text":1722,"children":3042},[3043,3044,3045,3046],{"id":1725,"depth":610,"text":1726},{"id":1784,"depth":610,"text":1785},{"id":1837,"depth":610,"text":1838},{"id":1979,"depth":610,"text":1980},{"id":2036,"depth":604,"text":2037},{"id":2100,"depth":604,"text":2101,"children":3049},[3050,3051,3052],{"id":2104,"depth":610,"text":2105},{"id":2152,"depth":610,"text":2153},{"id":2231,"depth":610,"text":2232},{"id":2310,"depth":604,"text":2311,"children":3054},[3055,3057,3059,3061],{"id":2321,"depth":610,"text":3056},"8.1 ON CONFLICT DO SELECT — Atomic get-or-create",{"id":2558,"depth":610,"text":3058},"8.2 GROUP BY ALL — Nhóm theo mọi cột non-aggregate",{"id":2686,"depth":610,"text":3060},"8.3 FOR PORTION OF — Cập nhật dữ liệu temporal",{"id":2868,"depth":610,"text":3062},"8.4 IGNORE NULLS cho Window Functions","Kiểu dữ liệu, DDL, DML, UPSERT (ON CONFLICT), RETURNING, GROUP BY ALL, FOR PORTION OF — gốc cho PostgreSQL 18 & 19.","md",null,{},{"title":178,"icon":181},{"title":422,"description":3063},"1Eb10AEB4qSXaWVGmd0cZZg_jT3zbkO23CzUG8y3wr8",[3071,3073],{"title":173,"path":204,"stem":205,"description":3072,"icon":176,"children":-1},"PostgreSQL 19 (Beta 1 — 04\u002F06\u002F2026) — landmark release với SQL\u002FPGQ graph queries, ON CONFLICT DO SELECT, GROUP BY ALL, temporal data, cùng nền tảng vững chắc từ PG 18.",{"title":183,"path":210,"stem":211,"description":3074,"icon":186,"children":-1},"JOIN, LATERAL, CTE recursive, window function, MVCC, transaction, isolation, EXPLAIN — gốc Postgres.",1783490365272]