{ "cells": [ { "cell_type": "code", "execution_count": 1, "id": "ac069784-fbb6-48c8-a547-11b96cade97b", "metadata": { "collapsed": true, "jupyter": { "outputs_hidden": true, "source_hidden": true }, "scrolled": true }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/tmp/ipykernel_218/1243805045.py:22: UserWarning: pandas only supports SQLAlchemy connectable (engine/connection) or database string URI or sqlite3 DBAPI2 connection. Other DBAPI2 objects are not tested. Please consider using SQLAlchemy.\n", " df = pd.read_sql(sql, conn)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " product_name total_images\n", "0 s2_l2a 2074896\n", "1 sentinel_2_c1_l2a 1137446\n", "2 landsat7_c2l2_sr 169065\n", "3 landsat7_c2l2_st 168410\n", "4 landsat8_c2l2_sr 122846\n", "5 landsat8_c2l2_st 120778\n", "6 landsat5_c2l2_sr 118583\n", "7 landsat5_c2l2_st 118406\n", "8 landsat8_c2l1 111513\n", "9 nasa_aqua_l2_oc 37232\n", "10 landsat9_c2l2_sr 30214\n", "11 landsat9_c2l2_st 29709\n", "12 landsat9_c2l1 18875\n", "13 nasa_aqua_l2_sst 12070\n", "14 ga_ls_mangrove_cover_cyear_3 5580\n", "15 sentinel1_grd_gamma0_10m_unsmooth 5488\n", "16 sentinel1_grd_gamma0_20m 4260\n", "17 copernicus_dem_30 1334\n", "18 global_mangrove_soc_2020_30m 784\n", "19 global_mangrove_soc_2000_30m 781\n", "20 global_mangrove_canopy_height_2015_12m 761\n", "21 sentinel1_grd_gamma0_10m 291\n", "22 lpdaac_nasadem 264\n", "23 cci_biomass_annual_v51 248\n", "24 esa_worldcover_2020 184\n", "25 esa_worldcover_2021 184\n", "26 global_tidal_marsh_soc_30m 41\n", "27 global_tidal_marsh_distribution_2020_10m 35\n", "28 copernicus_dem_fiji 26\n", "29 lpdaac_mod11a1v061_lste 2\n", "30 global_mangrove_soc_2000_100m 1\n" ] } ], "source": [ "import psycopg2\n", "import os\n", "import pandas as pd\n", "\n", "# Kết nối (như cũ)\n", "conn = psycopg2.connect(\n", " host=os.environ.get('DB_HOSTNAME'),\n", " user=os.environ.get('DB_USERNAME'),\n", " password=os.environ.get('DB_PASSWORD'),\n", " dbname=os.environ.get('DB_DATABASE')\n", ")\n", "\n", "# Đếm số lượng ảnh theo từng loại sản phẩm\n", "sql = \"\"\"\n", "SELECT t.name as product_name, count(*) as total_images\n", "FROM agdc.dataset d\n", "JOIN agdc.dataset_type t ON d.dataset_type_ref = t.id\n", "GROUP BY t.name\n", "ORDER BY total_images DESC;\n", "\"\"\"\n", "\n", "df = pd.read_sql(sql, conn)\n", "print(df)\n", "conn.close()" ] }, { "cell_type": "code", "execution_count": null, "id": "a0ae4051-3bfd-49fd-83fe-4c31c74338e2", "metadata": {}, "outputs": [], "source": [ "import psycopg2\n", "import os\n", "\n", "# 1. Lấy thông tin kết nối\n", "db_host = os.environ.get('DB_HOSTNAME')\n", "db_user = os.environ.get('DB_USERNAME')\n", "db_pass = os.environ.get('DB_PASSWORD')\n", "db_name = os.environ.get('DB_DATABASE')\n", "\n", "# 2. Danh sách các bảng cần lấy\n", "tables = [\n", " 'agdc.metadata_type',\n", " 'agdc.dataset_type',\n", " 'agdc.dataset_location',\n", " 'agdc.dataset'\n", "]\n", "\n", "print(\"Đang kết nối trực tiếp tới PostgreSQL...\")\n", "\n", "try:\n", " # Kết nối trực tiếp (Bỏ qua Pandas/SQLAlchemy)\n", " conn = psycopg2.connect(\n", " host=db_host,\n", " user=db_user,\n", " password=db_pass,\n", " dbname=db_name\n", " )\n", " cur = conn.cursor()\n", " print(\"Kết nối thành công!\\n\")\n", "\n", " for table_name in tables:\n", " print(f\"--> Đang xuất bảng: {table_name}\")\n", " \n", " # Tên file CSV đầu ra\n", " file_name = table_name.split('.')[1] + \".csv\"\n", " \n", " # Sử dụng lệnh COPY chuyên dụng của Postgres (Cực nhanh và chuẩn)\n", " # SQL: COPY (SELECT * FROM table) TO STDOUT WITH CSV HEADER\n", " sql = f\"COPY (SELECT * FROM {table_name}) TO STDOUT WITH CSV HEADER\"\n", " \n", " with open(file_name, 'w') as f:\n", " cur.copy_expert(sql, f)\n", " \n", " print(f\" Đã lưu xong: {file_name}\")\n", "\n", " cur.close()\n", " conn.close()\n", " print(\"\\nHOÀN TẤT! Bạn hãy tải 4 file CSV về máy.\")\n", "\n", "except Exception as e:\n", " print(f\"\\nCÓ LỖI XẢY RA: {e}\")" ] }, { "cell_type": "code", "execution_count": null, "id": "d139879e-c350-4986-a2e6-0d59099eb770", "metadata": { "collapsed": true, "jupyter": { "outputs_hidden": true, "source_hidden": true } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Đang kết nối...\n", "Kết nối thành công! Đang ước lượng nhanh...\n", "\n", "--- BẢNG DỰ TÍNH DUNG LƯỢNG (ƯỚC LƯỢNG) ---\n", "NĂM | SỐ ẢNH (ƯỚC TÍNH) | SIZE (CSV) \n", "------------------------------------------------------------\n", "2020 | ~ 21,448 | ~ 83.78 MB\n", "2021 | ~ 21,448 | ~ 83.78 MB\n", "2022 | ~ 21,448 | ~ 83.78 MB\n", "2023 | ~ 21,448 | ~ 83.78 MB\n", "2024 | ~ 21,448 | ~ 83.78 MB\n", "------------------------------------------------------------\n", "TỔNG CỘNG : ~ 107,240 ảnh (Khoảng 418.91 MB)\n", "------------------------------------------------------------\n", "(Lưu ý: Số liệu này là ước tính của Database, độ chính xác khoảng 80-90%)\n" ] }, { "name": "stdin", "output_type": "stream", "text": [ "\n", "Bạn có muốn bắt đầu tải không? (y/n): y\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\n", ">>> Đang tải bảng định nghĩa...\n", "\n", ">>> Đang tải dữ liệu chính...\n", "--> Năm 2020...\n" ] } ], "source": [ "import psycopg2\n", "import os\n", "import re # Thư viện xử lý chuỗi để lấy số liệu\n", "\n", "# --- CẤU HÌNH ---\n", "YEARS = [2020, 2021, 2022, 2023, 2024]\n", "AVG_ROW_SIZE_KB = 4 \n", "\n", "db_host = os.environ.get('DB_HOSTNAME')\n", "db_user = os.environ.get('DB_USERNAME')\n", "db_pass = os.environ.get('DB_PASSWORD')\n", "db_name = os.environ.get('DB_DATABASE')\n", "\n", "print(\"Đang kết nối...\")\n", "\n", "try:\n", " conn = psycopg2.connect(\n", " host=db_host, user=db_user, password=db_pass, dbname=db_name\n", " )\n", " cur = conn.cursor()\n", " print(\"Kết nối thành công! Đang ước lượng nhanh...\\n\")\n", "\n", " # ==========================================\n", " # BƯỚC 1: ƯỚC LƯỢNG SIÊU TỐC (INSTANT ESTIMATE)\n", " # ==========================================\n", " print(\"--- BẢNG DỰ TÍNH DUNG LƯỢNG (ƯỚC LƯỢNG) ---\")\n", " print(f\"{'NĂM':<10} | {'SỐ ẢNH (ƯỚC TÍNH)':<20} | {'SIZE (CSV)':<20}\")\n", " print(\"-\" * 60)\n", "\n", " total_est_rows = 0\n", "\n", " for year in YEARS:\n", " # Mẹo: Dùng EXPLAIN để lấy số liệu ước tính từ Query Planner\n", " # Nó sẽ trả về chuỗi kiểu: \"Seq Scan on dataset ... (rows=12345 ...)\"\n", " sql_estimate = f\"\"\"\n", " EXPLAIN SELECT 1 FROM agdc.dataset\n", " WHERE (metadata->'properties'->>'datetime')::timestamp >= '{year}-01-01'\n", " AND (metadata->'properties'->>'datetime')::timestamp <= '{year}-12-31 23:59:59'\n", " \"\"\"\n", " cur.execute(sql_estimate)\n", " explain_result = cur.fetchone()[0] # Lấy dòng đầu tiên của kết quả EXPLAIN\n", " \n", " # Dùng Regex để bắt lấy con số sau chữ \"rows=\"\n", " match = re.search(r\"rows=(\\d+)\", explain_result)\n", " if match:\n", " count = int(match.group(1))\n", " else:\n", " count = 0 # Không bắt được số\n", " \n", " total_est_rows += count\n", " est_size_mb = (count * AVG_ROW_SIZE_KB) / 1024\n", " \n", " print(f\"{year:<10} | ~ {count:<18,} | ~ {est_size_mb:.2f} MB\")\n", "\n", " print(\"-\" * 60)\n", " print(f\"TỔNG CỘNG : ~ {total_est_rows:,} ảnh (Khoảng {(total_est_rows * AVG_ROW_SIZE_KB)/1024:.2f} MB)\")\n", " print(\"-\" * 60)\n", " print(\"(Lưu ý: Số liệu này là ước tính của Database, độ chính xác khoảng 80-90%)\")\n", "\n", " # ==========================================\n", " # QUYẾT ĐỊNH TẢI\n", " # ==========================================\n", " check = input(\"\\nBạn có muốn bắt đầu tải không? (y/n): \")\n", " if check.lower() != 'y':\n", " print(\"Đã hủy.\")\n", " exit()\n", "\n", " # ==========================================\n", " # BƯỚC 2 & 3: TẢI DỮ LIỆU (Giữ nguyên logic cũ)\n", " # ==========================================\n", " # ... (Phần code tải small_tables và tải dữ liệu chính giữ nguyên như cũ) ...\n", " # Để code gọn, mình viết tiếp phần tải ở dưới đây:\n", " \n", " # 2. Tải bảng nhỏ\n", " print(\"\\n>>> Đang tải bảng định nghĩa...\")\n", " for tb in ['agdc.metadata_type', 'agdc.dataset_type']:\n", " f_name = tb.split('.')[1] + \".csv\"\n", " with open(f_name, 'w') as f:\n", " cur.copy_expert(f\"COPY (SELECT * FROM {tb}) TO STDOUT WITH CSV HEADER\", f)\n", " \n", " # 3. Tải dữ liệu chính\n", " print(\"\\n>>> Đang tải dữ liệu chính...\")\n", " for year in YEARS:\n", " print(f\"--> Năm {year}...\")\n", " \n", " # Dataset\n", " f_ds = f\"dataset_{year}.csv\"\n", " sql_ds = f\"\"\"\n", " COPY (SELECT * FROM agdc.dataset \n", " WHERE (metadata->'properties'->>'datetime')::timestamp >= '{year}-01-01' \n", " AND (metadata->'properties'->>'datetime')::timestamp <= '{year}-12-31 23:59:59'\n", " ) TO STDOUT WITH CSV HEADER\"\"\"\n", " with open(f_ds, 'w') as f: cur.copy_expert(sql_ds, f)\n", " \n", " # Location\n", " f_loc = f\"dataset_location_{year}.csv\"\n", " sql_loc = f\"\"\"\n", " COPY (SELECT l.* FROM agdc.dataset_location l JOIN agdc.dataset d ON l.dataset_ref = d.id\n", " WHERE (d.metadata->'properties'->>'datetime')::timestamp >= '{year}-01-01' \n", " AND (d.metadata->'properties'->>'datetime')::timestamp <= '{year}-12-31 23:59:59'\n", " ) TO STDOUT WITH CSV HEADER\"\"\"\n", " with open(f_loc, 'w') as f: cur.copy_expert(sql_loc, f)\n", " \n", " print(\"\\nHOÀN TẤT TOÀN BỘ!\")\n", " cur.close()\n", " conn.close()\n", "\n", "except Exception as e:\n", " print(f\"\\nCÓ LỖI: {e}\")" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.3" } }, "nbformat": 4, "nbformat_minor": 5 }