一招教你如何高效批量導入與更新數據

来源:https://www.cnblogs.com/huaweiyun/archive/2022/09/16/16699289.html
-Advertisement-
Play Games

摘要:如果你的數據量很大,想儘快完成任務執行,可否有其他方案?那一定不要錯過GaussDB(DWS)的MERGE INTO功能。 本文分享自華為雲社區《一招教你如何高效批量導入與更新數據》,作者: acydy。 當前GaussDB(DWS)提供了MERGE INTO功能。本篇文章介紹MERGE IN ...


摘要:如果你的數據量很大,想儘快完成任務執行,可否有其他方案?那一定不要錯過GaussDB(DWS)的MERGE INTO功能。

本文分享自華為雲社區《一招教你如何高效批量導入與更新數據》,作者: acydy。

當前GaussDB(DWS)提供了MERGE INTO功能。本篇文章介紹MERGE INTO功能與基本用法。

前言

如果有一張表,我們既想對它更新,又想對它插入應該如何操作? 可以使用UPDATE和INSERT完成你的目標。

如果你的數據量很大,想儘快完成任務執行,可否有其他方案?那一定不要錯過GaussDB(DWS)的MERGE INTO功能。

MERGE INTO 概念

MERGE INTO是SQL 2003引入的標準。

If a table T, as well as being updatable, is insertable-into, then rows can be inserted into it (subject to applicable Access Rules and Conformance Rules). The primary effect of an <insert statement> on T is to insert into T each of the zero or more rows contained in a specified table. The primary effect of a <merge statement> on T is to replace zero or more rows in T with specified rows and/or to insert into T zero or more specified rows, depending on the result of a <search condition> and on whether one or both of <merge when matched clause> and <merge when not matched clause> are specified.

一張表在一條語句裡面既可以被更新,也可以被插入。是否被更新還是插入取決於search condition的結果和指定的merge when matched clause(當condition匹配時做什麼操作)和merge when not matched clause(當condition不匹配時做什麼操作)語法。

SQL 2008進行了擴展,可以使用多個MATCHED 和NOT MATCHED 。

MERGE has been extended to support multiple MATCHED and NOT MATCHED clauses, each accompanied by a search condition, that gives much greater flexibility in the coding of complex MERGE statements to handle update conflicts.

MERGE INTO 命令涉及到兩張表。目標表:被插入或者更新的表。源表:用於跟目標表進行匹配的表,目標表的數據來源。

MERGE INTO語句將目標表和源表中數據針對關聯條件進行匹配,若關聯條件匹配時對目標表進行UPDATE,無法匹配時對目標表執行INSERT。

使用場景:當業務中需要將一個表中大量數據添加到現有表時,使用MERGE INTO 可以高效地將數據導入,避免多次INSERT+UPDATE操作。

MERGE INTO 語法

GaussDB(DWS) MERGE INTO 語法如下:

MERGE INTO table_name [ [ AS ] alias ]
USING { { table_name | view_name } | subquery } [ [ AS ] alias ]
ON ( condition )
[
 WHEN MATCHED THEN
 UPDATE SET { column_name = { expression | DEFAULT } |
 ( column_name [, ...] ) = ( { expression | DEFAULT } [, ...] ) } [, ...]
 [ WHERE condition ]
]
[
 WHEN NOT MATCHED THEN
 INSERT { DEFAULT VALUES |
 [ ( column_name [, ...] ) ] VALUES ( { expression | DEFAULT } [, ...] ) [, ...] [ WHERE condition ] }
];
  • INTO 指定目標表。
  • USING 指定源表。源表可以是普通表,也可以是子查詢。
  • ON 關聯條件,用於指定目標表和源表的關聯條件。
  • WHEN MATCHED 當源表和目標表中數據可以匹配關聯條件時,選擇WHEN MATCHED子句執行UPDATE操作。
  • WHEN NOT MATCHED 當源表和目標表中數據無法匹配關聯條件時,選擇WHEN NOT MATCHED子句執行INSERT操作。
    • WHEN MATCHED,WHEN NOT MATCHED 可以預設一個,不能指定多個。
    • WHEN MATCHED,WHEN NOT MATCHED 可以使用WHERE進行條件過濾。
    • WHEN MATCHED,WHEN NOT MATCHED 順序可以交換。

實戰應用

首先創建好下麵幾張表,用於執行MREGE INTO 操作。

gaussdb=# CREATE TABLE dst (
 product_id INT,
 product_name VARCHAR(20),
  category VARCHAR(20),
  total INT
) DISTRIBUTE BY HASH(product_id);
gaussdb=# CREATE TABLE dst_data (
 product_id INT,
 product_name VARCHAR(20),
  category VARCHAR(20),
  total INT
) DISTRIBUTE BY HASH(product_id);
gaussdb=# CREATE TABLE src (
 product_id INT,
 product_name VARCHAR(20),
  category VARCHAR(20),
  total INT
) DISTRIBUTE BY HASH(product_id);
gaussdb=# INSERT INTO dst_data VALUES(1601,'lamaze','toys',100),(1600,'play gym','toys',100),(1502,'olympus','electrncs',100),(1501,'vivitar','electrnc',100),(1666,'harry potter','dvd',100);
gaussdb=# INSERT INTO src VALUES(1700,'wait interface','books',200),(1666,'harry potter','toys',200),(1601,'lamaze','toys',200),(1502,'olympus camera','electrncs',200);
gaussdb=# INSERT INTO dst SELECT * FROM dst_data;

同時指定WHEN MATCHED 與WHEN NOT MATCHED

  • 查看計劃,看下MERGE INTO是如何執行的。

MERGE INTO轉化成JOIN將兩個表進行關聯處理,關聯條件就是ON後指定的條件。

gaussdb=# EXPLAIN (COSTS off)
MERGE INTO dst x
USING src y
ON x.product_id = y.product_id
WHEN MATCHED THEN
 UPDATE SET product_name = y.product_name, category = y.category, total = y.total
WHEN NOT MATCHED THEN
 INSERT VALUES (y.product_id, y.product_name, y.category, y.total);
                    QUERY PLAN
--------------------------------------------------
  id |                operation
-----+--------------------------------------------
 1 | ->  Streaming (type: GATHER)
 2 | -> Merge on dst x
 3 | ->  Streaming(type: REDISTRIBUTE)
 4 | -> Hash Left Join (5, 6)
 5 | ->  Seq Scan on src y
 6 | -> Hash
 7 | ->  Seq Scan on dst x
  Predicate Information (identified by plan id)
 ------------------------------------------------
 4 --Hash Left Join (5, 6)
 Hash Cond: (y.product_id = x.product_id)
(14 rows)

為什麼這裡轉化成了LEFT JOIN?

由於需要在目標表與源表匹配時更新目標表,不匹配時向目標表插入數據。也就是源表的一部分數據用於更新目標表,另一部分用於向目標表插入。與LEFT JOIN語義是相似的。

 5 --Seq Scan on public.src y
         Output: y.product_id, y.product_name, y.category, y.total, y.ctid
         Distribute Key: y.product_id
 6 --Hash
         Output: x.product_id, x.product_name, x.category, x.total, x.ctid, x.xc_node_id
 7 --Seq Scan on public.dst x
         Output: x.product_id, x.product_name, x.category, x.total, x.ctid, x.xc_node_id
         Distribute Key: x.product_id
  • 執行MERGE INTO,查看結果。

兩張表在product_id是1502,1601,1666時可以關聯,所以這三條記錄被更新。src表product_id是1700時未匹配,插入此條記錄。其他未修改。

gaussdb=# SELECT * FROM dst ORDER BY 1;
 product_id | product_name | category  | total
------------+--------------+-----------+-------
 1501 | vivitar | electrnc | 100
 1502 | olympus | electrncs | 100
 1600 | play gym     | toys      | 100 
 1601 | lamaze | toys      | 100
 1666 | harry potter | dvd | 100 
(5 rows)
gaussdb=# SELECT * FROM src ORDER BY 1;
 product_id | product_name | category  | total
------------+----------------+-----------+-------
 1502 | olympus camera | electrncs | 200
 1601 | lamaze | toys      | 200 
 1666 | harry potter   | toys      | 200
 1700 | wait interface | books     | 200 
(4 rows)
gaussdb=# MERGE INTO dst x
USING src y
ON x.product_id = y.product_id
WHEN MATCHED THEN
 UPDATE SET product_name = y.product_name, category = y.category, total = y.total
WHEN NOT MATCHED THEN
 INSERT VALUES (y.product_id, y.product_name, y.category, y.total);
MERGE 4
gaussdb=# SELECT * FROM dst ORDER BY 1;
 product_id | product_name | category  | total
------------+----------------+-----------+-------
 1501 | vivitar | electrnc | 100 -- 未修改
 1502 | olympus camera | electrncs | 200 -- 更新
 1600 | play gym       | toys      | 100 -- 未修改
 1601 | lamaze | toys      | 200 -- 更新
 1666 | harry potter   | toys      | 200 -- 更新
 1700 | wait interface | books     | 200 -- 插入
(6 rows)
  • 查看具體UPDATE、INSERT個數

可以通過EXPLAIN PERFORMANCE或者EXPLAIN ANALYZE查看UPDATE、INSERT各自個數。(這裡僅顯示必要部分)

在Predicate Information部分可以看到總共插入一條,更新三條。

在Datanode Information部分可以看到每個節點的信息。datanode1上更新2條,datanode2上插入一條,更新1條。

gaussdb=# EXPLAIN PERFORMANCE
MERGE INTO dst x
USING src y
ON x.product_id = y.product_id
WHEN MATCHED THEN
 UPDATE SET product_name = y.product_name, category = y.category, total = y.total
WHEN NOT MATCHED THEN
 INSERT VALUES (y.product_id, y.product_name, y.category, y.total);
  Predicate Information (identified by plan id)
 ------------------------------------------------
 2 --Merge on public.dst x
 Merge Inserted: 1
 Merge Updated: 3
 Datanode Information (identified by plan id)
 ---------------------------------------------------------------------------------------
 2 --Merge on public.dst x
         datanode1 (Tuple Inserted 0, Tuple Updated 2)
         datanode2 (Tuple Inserted 1, Tuple Updated 1) 

省略WHEN NOT MATCHED 部分。

  • 這裡由於沒有WHEN NOT MATCHED部分,在兩個表不匹配時不需要執行任何操作,也就不需要源表這部分的數據,所有隻需要inner join即可。
gaussdb=# EXPLAIN (COSTS off)
MERGE INTO dst x
USING src y
ON x.product_id = y.product_id
WHEN MATCHED THEN
 UPDATE SET product_name = y.product_name, category = y.category, total = y.total;
                    QUERY PLAN
--------------------------------------------------
  id |             operation
 ----+-----------------------------------
 1 | ->  Streaming (type: GATHER)
 2 | -> Merge on dst x
 3 | -> Hash Join (4,5)
 4 | ->  Seq Scan on dst x
 5 | -> Hash
 6 | ->  Seq Scan on src y
  Predicate Information (identified by plan id)
 ------------------------------------------------
 3 --Hash Join (4,5)
 Hash Cond: (x.product_id = y.product_id)
(13 rows)
  • 執行後查看結果。MERGE INTO只操作了3條數據。
gaussdb=# truncate dst;
gaussdb=# INSERT INTO dst SELECT * FROM dst_data;
gaussdb=# MERGE INTO dst x
USING src y
ON x.product_id = y.product_id
WHEN MATCHED THEN
 UPDATE SET product_name = y.product_name, category = y.category, total = y.total;
MERGE 3
gaussdb=# SELECT * FROM dst;
 product_id | product_name | category  | total
------------+----------------+-----------+-------
 1501 | vivitar | electrnc | 100 -- 未修改
 1502 | olympus camera | electrncs | 200 -- 更新
 1600 | play gym       | toys      | 100 -- 未修改
 1601 | lamaze | toys      | 200 -- 更新
 1666 | harry potter   | toys      | 200 -- 更新
(5 rows)

省略WHEN NOT MATCHED

  • 只有在不匹配時進行插入。結果中沒有數據被更新。
gaussdb=# EXPLAIN (COSTS off)
MERGE INTO dst x
USING src y
ON x.product_id = y.product_id
WHEN NOT MATCHED THEN
 INSERT VALUES (y.product_id, y.product_name, y.category, y.total);
                    QUERY PLAN
--------------------------------------------------
  id |                operation
 ----+-----------------------------------------
 1 | ->  Streaming (type: GATHER)
 2 | -> Merge on dst x
 3 | ->  Streaming(type: REDISTRIBUTE)
 4 | -> Hash Left Join (5, 6)
 5 | ->  Seq Scan on src y
 6 | -> Hash
 7 | ->  Seq Scan on dst x
  Predicate Information (identified by plan id)
 ------------------------------------------------
 4 --Hash Left Join (5, 6)
 Hash Cond: (y.product_id = x.product_id)
(14 rows)
gaussdb=# truncate dst;
gaussdb=# INSERT INTO dst SELECT * FROM dst_data;
gaussdb=# MERGE INTO dst x
USING src y
ON x.product_id = y.product_id
WHEN NOT MATCHED THEN
 INSERT VALUES (y.product_id, y.product_name, y.category, y.total);
MERGE 1
gaussdb=# SELECT * FROM dst ORDER BY 1;
 product_id | product_name | category  | total
------------+----------------+-----------+-------
 1501 | vivitar | electrnc | 100 -- 未修改
 1502 | olympus | electrncs | 100 -- 未修改
 1600 | play gym       | toys      | 100 -- 未修改
 1601 | lamaze | toys      | 100 -- 未修改
 1666 | harry potter   | dvd | 100 -- 未修改
 1700 | wait interface | books     | 200 -- 插入
(6 rows)

WHERE過濾條件

語義是在進行更新或者插入前判斷當前行是否滿足過濾條件,如果不滿足,就不進行更新或者插入。如果對於欄位不想被更新,需要指定過濾條件。

下麵例子在兩表可關聯時,只會更新product_name = 'olympus’的行。在兩表無法關聯時且源表的product_id != 1700時才會進行插入。

gaussdb=# truncate dst;
gaussdb=# INSERT INTO dst SELECT * FROM dst_data;
gaussdb=# MERGE INTO dst x
USING src y
ON x.product_id = y.product_id
WHEN MATCHED THEN
 UPDATE SET product_name = y.product_name, category = y.category, total = y.total
 WHERE x.product_name = 'olympus'
WHEN NOT MATCHED THEN
 INSERT VALUES (y.product_id, y.product_name, y.category, y.total) WHERE y.product_id != 1700;
MERGE 1
gaussdb=# SELECT * FROM dst ORDER BY 1;
SELECT * FROM dst ORDER BY 1;
 product_id | product_name | category  | total
------------+----------------+-----------+-------
 1501 | vivitar | electrnc | 100
 1502 | olympus camera | electrncs | 200
 1600 | play gym       | toys      | 100
 1601 | lamaze | toys      | 100
 1666 | harry potter   | dvd | 100
(5 rows)

子查詢

在USING部分可以使用子查詢,進行更複雜的關聯操作。

  • 對源表進行聚合操作的結果再與目標表匹配
MERGE INTO dst x
USING (
 SELECT product_id, product_name, category, sum(total) AS total FROM src group by product_id, product_name, category
) y
ON x.product_id = y.product_id
WHEN MATCHED THEN
 UPDATE SET product_name = x.product_name, category = x.category, total = x.total
WHEN NOT MATCHED THEN
 INSERT VALUES (y.product_id, y.product_name, y.category, y.total + 200);
  • 多個表UNION後的結果再與目標表匹配
MERGE INTO dst x
USING (
 SELECT 1501 AS product_id, 'vivitar 35mm' AS product_name, 'electrncs' AS category, 100 AS total UNION ALL
 SELECT 1666 AS product_id, 'harry potter' AS product_name, 'dvd' AS category, 100 AS total
) y
ON x.product_id = y.product_id
WHEN MATCHED THEN
 UPDATE SET product_name = x.product_name, category = x.category, total = x.total
WHEN NOT MATCHED THEN
 INSERT VALUES (y.product_id, y.product_name, y.category, y.total + 200);

存儲過程

gaussdb=# CREATE OR REPLACE PROCEDURE store_procedure1()
AS
BEGIN
 MERGE INTO dst x
 USING src y
 ON x.product_id = y.product_id
 WHEN MATCHED THEN
 UPDATE SET product_name = y.product_name, category = y.category, total = y.total;
END;
/
CREATE PROCEDURE
gaussdb=# CALL store_procedure1();

MERGE INTO背後原理

上文提到了MREGE INTO轉化成LEFT JOIN或者INNER JOIN將目標表和源表進行關聯。那麼如何知道某一行要進行更新還是插入?

通過EXPLAIN VERBOSE查看運算元的輸出。掃描兩張表時都輸出了ctid列。那麼ctid列有什麼作用呢?

 5 --Seq Scan on public.src y
         Output: y.product_id, y.product_name, y.category, y.total, y.ctid
         Distribute Key: y.product_id
 6 --Hash
         Output: x.product_id, x.product_name, x.category, x.total, x.ctid, x.xc_node_id
 7 --Seq Scan on public.dst x
         Output: x.product_id, x.product_name, x.category, x.total, x.ctid, x.xc_node_id
         Distribute Key: x.product_id

ctid標識了這一行在存儲上具體位置,知道了這個位置就可以對這個位置的數據進行更新。GaussDB(DWS)作為MPP分散式資料庫,還需要知道節點的信息(xc_node_id)。UPDATE操作需要這兩個值。

在MREGE INTO這裡ctid還另有妙用。當目標表匹配時需要更新,這是就保留本行ctid值。如果無法匹配,插入即可。就不需要ctid,此時可認識ctid值是NULL。根據LEFT JOIN輸出的ctid結果是否為NULL,最終決定本行該被更新還是插入。

這樣在兩張表做完JOIN操作後,根據JOIN後輸出的ctid列,更新或者插入某一行。

註意事項

使用MERGE INTO時要註意匹配條件是否合適。如果不註意,容易造成數據被非預期更新,可能整張表被更新。

總結

GAUSSDB(DWS)提供了高效的數據導入的功能MERGE INTO,對於數據倉庫是一項非常關鍵的功能。可以使用MERGE INTO 同時更新和插入一張表,在數據量非常大的情況下也能很快完成地數據導入。

想瞭解GuassDB(DWS)更多信息,歡迎微信搜索“GaussDB DWS”關註微信公眾號,和您分享最新最全的PB級數倉黑科技,後臺還可獲取眾多學習資料哦~

 

點擊關註,第一時間瞭解華為雲新鮮技術~


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 日常工作中有時候會遇到需要將程式直接在伺服器上運行,而不依賴於 IIS 托管的情況,直接運行有兩種方式,一種是部署為 服務模式,另一種則是 直接啟動 .NET 發佈之後的 exe 文件以 控制台模式運行,控制台模式運行主要問題是伺服器在重新啟動之後不會自動啟動,當然也可以選擇配置 Windows 計 ...
  • 今天我們來介紹一下 Bootstrap Blazor 中 Table 組件的虛擬滾動行,什麼是虛擬滾動呢,我查到的解釋是:只渲染可視區域的列表項,非可見區域的 完全不渲染,在滾動條滾動時動態更新列表項。 然後很明顯,在實際應用中不可能實現“非可見區域的 完全不渲染”,這樣的體驗效果太差了,下拉直接空 ...
  • 在VMware虛擬機下centOS7下配置橋接網路 首先,在以下的配置都操作好之後,要確認宿主機的網路連接方式, 若為需要認證的網路,則可能會出現宿主機與虛擬機能互相ping,但虛擬機不能ping外網的情況 一、虛擬機配置 確保虛擬機為關機的狀態,在VMware中編輯一欄打開虛擬網路編輯器,點擊更改 ...
  • 數據結構概述: 程式 = 數據結構 + 演算法 演算法:一些計算方法,解決問題的流程/步驟(順序、分支、迴圈......),通俗的說,演算法相當於邏輯,小部分已經被人們發掘出來了(這裡的小部分指的是書本上講的一些經典的解決一類問題的方法),解決問題的一種模式。 數據結構:將數據按照某一種特定的結構(方法) ...
  • Set A Light 3D Studio for Mac是一款可以幫助攝影工作者對3D室內攝影進行燈光位置調整的軟體,你可以清楚的瞭解到不同位置的燈光怎麼佈局。Set A Light 3D Studio Mac模擬影樓的效果,使用它可以預先在PC或Mac上搭建的照明設置,逼真地模擬預期的畫面效果和 ...
  • sed用法 基礎sed命令 sed命令的基本語法 sed OPTIONS… [SCRIPT] [INPUTFILE…] 常用的選項: -n,–quiet: 不輸出模式空間中的內容 -i: 直接編輯原文件,預設不對原文件進行操作 -e: 可以使用多個命令(腳本)進行操作 -f /path/from/s ...
  • 超級熱鍵可通過簡單編程 —— 自動化完成複雜操作,提升效率。 ▶ 快速上手 本教程需要一個很小的開源軟體 ImTip ( 體積 639 KB ), 請右鍵點開 ImTip 托盤菜單,然後點擊「管理超級熱鍵」: 然後將熱鍵配置改為如下代碼,並且勾選「啟用超級熱鍵」,再點擊「保存」按鈕使熱鍵生效。 // ...
  • Linux的哲學思想 優勢 一切都是一個文件。(包括硬體,文本,二進位,源代 碼) 系統中擁有小型,單一用途的程式。(一個程式只負責 做好自己的本職工作) 當遇到複雜任務,通過不同功能用途的程式組合起來 完成。 輕量級,一臺服務 dhcp ip 資料庫服務 網 頁 避免令人困惑的用戶界面就是沒有複雜 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...