最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

PostgreSQL  JOIN 聯(lián)表查詢(xún)實(shí)戰(zhàn)演練(內(nèi)連接 / 外連接 / 交叉連接)

 更新時(shí)間:2026年03月09日 16:31:24   作者:Jinkxs  
本文介紹了PostgreSQL中的JOIN查詢(xún),包括INNER JOIN、LEFT JOIN、RIGHT JOIN、FULL JOIN和CROSS JOIN,通過(guò)實(shí)際示例和Java代碼,展示了如何在Java應(yīng)用程序中執(zhí)行這些JOIN操作,感興趣的朋友跟隨小編一起看看吧

在現(xiàn)代數(shù)據(jù)庫(kù)系統(tǒng)中,數(shù)據(jù)通常分散在多個(gè)相關(guān)的表中,以減少冗余并提高效率。然而,當(dāng)我們需要從這些獨(dú)立的表中獲取綜合信息時(shí),就需要使用 JOIN 查詢(xún)。PostgreSQL 提供了多種 JOIN 類(lèi)型,包括內(nèi)連接(INNER JOIN)、左外連接(LEFT OUTER JOIN)、右外連接(RIGHT OUTER JOIN)、全外連接(FULL OUTER JOIN)以及交叉連接(CROSS JOIN)。掌握這些 JOIN 操作是進(jìn)行有效數(shù)據(jù)庫(kù)查詢(xún)和數(shù)據(jù)分析的關(guān)鍵技能。本文將深入探討 PostgreSQL 中的 JOIN 查詢(xún),并通過(guò)豐富的 Java 代碼示例來(lái)展示如何在實(shí)際應(yīng)用中運(yùn)用這些技術(shù)。

一、JOIN 基礎(chǔ)概念與重要性 

1.1 什么是 JOIN?

JOIN 是 SQL 查詢(xún)中用于組合兩個(gè)或多個(gè)表中行的機(jī)制。它基于相關(guān)列之間的關(guān)系(通常是主鍵與外鍵的關(guān)系)來(lái)合并數(shù)據(jù)。通過(guò) JOIN,我們可以從多個(gè)表中檢索相關(guān)的數(shù)據(jù),形成一個(gè)邏輯上的單一視圖,這對(duì)于構(gòu)建復(fù)雜的報(bào)表和分析至關(guān)重要。

想象一個(gè)電子商務(wù)系統(tǒng),它有兩個(gè)主要表:customers(客戶(hù)表)和 orders(訂單表)。customers 表包含客戶(hù)的姓名、地址等信息,而 orders 表則包含訂單號(hào)、下單日期、客戶(hù) ID(外鍵)等信息。如果我們想獲取某個(gè)客戶(hù)的訂單詳情,就需要將這兩個(gè)表通過(guò)客戶(hù) ID 進(jìn)行關(guān)聯(lián)。

1.2 JOIN 的必要性

  • 數(shù)據(jù)完整性: 在規(guī)范化數(shù)據(jù)庫(kù)設(shè)計(jì)中,數(shù)據(jù)通常被拆分到不同的表中以避免冗余。JOIN 是恢復(fù)完整信息的橋梁。
  • 業(yè)務(wù)邏輯: 很多業(yè)務(wù)需求需要跨表數(shù)據(jù),例如“查找某個(gè)訂單的所有客戶(hù)信息”或“統(tǒng)計(jì)每個(gè)客戶(hù)的訂單總數(shù)”。
  • 性能優(yōu)化: 相比于將所有數(shù)據(jù)存儲(chǔ)在一個(gè)大表中,通過(guò) JOIN 查詢(xún)可以更有效地利用索引和緩存。

1.3 JOIN 的基本語(yǔ)法

SELECT columns
FROM table1
JOIN table2 ON table1.column = table2.column
WHERE conditions;
  • SELECT: 指定要返回的列。
  • FROM table1: 指定主表(左表)。
  • JOIN table2: 指定要連接的第二個(gè)表(右表)。
  • ON table1.column = table2.column: 指定連接條件,即兩個(gè)表中相關(guān)聯(lián)的列。
  • WHERE conditions: 可選的過(guò)濾條件。

二、內(nèi)連接 (INNER JOIN) 

2.1 內(nèi)連接的工作原理

內(nèi)連接(INNER JOIN)是最常用的 JOIN 類(lèi)型。它返回兩個(gè)表中都存在匹配記錄的行。換句話(huà)說(shuō),只有當(dāng)左表和右表的連接字段都有對(duì)應(yīng)值時(shí),才會(huì)將這兩行組合成結(jié)果集的一行。如果某一行在其中一個(gè)表中沒(méi)有匹配項(xiàng),則該行不會(huì)出現(xiàn)在最終結(jié)果中。

2.2 實(shí)踐:創(chuàng)建示例表

為了更好地演示,我們先創(chuàng)建兩個(gè)簡(jiǎn)單的示例表:employees(員工表)和 departments(部門(mén)表)。

-- 創(chuàng)建部門(mén)表
CREATE TABLE departments (
    dept_id SERIAL PRIMARY KEY,
    dept_name VARCHAR(100) NOT NULL
);
-- 創(chuàng)建員工表
CREATE TABLE employees (
    emp_id SERIAL PRIMARY KEY,
    emp_name VARCHAR(100) NOT NULL,
    dept_id INT, -- 外鍵關(guān)聯(lián)到 departments 表
    salary DECIMAL(10, 2),
    FOREIGN KEY (dept_id) REFERENCES departments(dept_id)
);
-- 插入部門(mén)數(shù)據(jù)
INSERT INTO departments (dept_name) VALUES
('Human Resources'),
('Engineering'),
('Marketing'),
('Finance');
-- 插入員工數(shù)據(jù)
INSERT INTO employees (emp_name, dept_id, salary) VALUES
('Alice Johnson', 1, 75000.00),
('Bob Smith', 2, 85000.00),
('Carol Davis', 2, 90000.00),
('David Wilson', 3, 65000.00),
('Eve Brown', 1, 70000.00),
('Frank Miller', 4, 80000.00),
('Grace Lee', NULL, 55000.00); -- Grace 沒(méi)有分配部門(mén)

這個(gè)示例模擬了一個(gè)公司結(jié)構(gòu):有員工和部門(mén),其中員工表通過(guò) dept_id 字段與部門(mén)表關(guān)聯(lián)。

2.3 INNER JOIN 查詢(xún)示例

現(xiàn)在,我們使用 INNER JOIN 來(lái)獲取所有有部門(mén)的員工及其部門(mén)名稱(chēng):

SELECT e.emp_name, e.salary, d.dept_name
FROM employees e
INNER JOIN departments d ON e.dept_id = d.dept_id
ORDER BY e.emp_name;

解釋:

  • SELECT e.emp_name, e.salary, d.dept_name: 選擇員工姓名、薪資和對(duì)應(yīng)的部門(mén)名稱(chēng)。
  • FROM employees e: 主表是 employees,并為其設(shè)置別名 e。
  • INNER JOIN departments d: 連接 departments 表,別名 d。
  • ON e.dept_id = d.dept_id: 連接條件,員工表的 dept_id 等于部門(mén)表的 dept_id。
  • ORDER BY e.emp_name: 按員工姓名排序。

執(zhí)行結(jié)果:

emp_namesalarydept_name
Alice Johnson75000.00Human Resources
Bob Smith85000.00Engineering
Carol Davis90000.00Engineering
David Wilson65000.00Marketing
Eve Brown70000.00Human Resources
Frank Miller80000.00Finance

注意: 員工 “Grace Lee” 沒(méi)有部門(mén) (dept_id 為 NULL),因此沒(méi)有出現(xiàn)在結(jié)果中。這就是 INNER JOIN 的特性:只返回匹配的行。

2.4 INNER JOIN 與其他 JOIN 的對(duì)比

INNER JOIN 與 LEFT JOIN 的區(qū)別在于,LEFT JOIN 會(huì)保留左表中沒(méi)有匹配項(xiàng)的行(用 NULL 填充右表字段),而 INNER JOIN 會(huì)完全忽略這些行。

三、左外連接 (LEFT OUTER JOIN) 

3.1 左外連接的工作原理

左外連接(LEFT OUTER JOIN)會(huì)返回左表中的所有行,無(wú)論右表中是否存在匹配的行。如果右表中沒(méi)有匹配項(xiàng),則結(jié)果集中右表的字段將填充為 NULL。

3.2 LEFT JOIN 查詢(xún)示例

繼續(xù)使用上面的示例,我們想看看所有員工,包括那些沒(méi)有分配部門(mén)的員工:

SELECT e.emp_name, e.salary, d.dept_name
FROM employees e
LEFT JOIN departments d ON e.dept_id = d.dept_id
ORDER BY e.emp_name;

解釋:

  • LEFT JOIN departments d: 使用 LEFT JOIN 連接部門(mén)表。
  • 其他部分與 INNER JOIN 相同。

執(zhí)行結(jié)果:

emp_namesalarydept_name
Alice Johnson75000.00Human Resources
Bob Smith85000.00Engineering
Carol Davis90000.00Engineering
David Wilson65000.00Marketing
Eve Brown70000.00Human Resources
Frank Miller80000.00Finance
Grace Lee55000.00NULL

注意: “Grace Lee” 出現(xiàn)在結(jié)果中,盡管她的 dept_id 為 NULL。她的 dept_name 字段顯示為 NULL,表示她沒(méi)有分配到任何部門(mén)。

3.3 實(shí)際應(yīng)用場(chǎng)景

LEFT JOIN 在以下場(chǎng)景中非常有用:

  • 獲取完整列表: 當(dāng)你需要獲取一個(gè)表的所有記錄,并附帶另一個(gè)表的相關(guān)信息時(shí)(如獲取所有員工及其部門(mén)信息)。
  • 查找缺失數(shù)據(jù): 可以輕松識(shí)別哪些記錄在關(guān)聯(lián)表中找不到匹配項(xiàng)(例如,哪些員工沒(méi)有分配部門(mén))。

四、右外連接 (RIGHT OUTER JOIN) 

4.1 右外連接的工作原理

右外連接(RIGHT OUTER JOIN)與左外連接相反。它會(huì)返回右表中的所有行,無(wú)論左表中是否存在匹配的行。如果左表中沒(méi)有匹配項(xiàng),則結(jié)果集中左表的字段將填充為 NULL。

4.2 RIGHT JOIN 查詢(xún)示例

雖然在我們的示例中 departments 表沒(méi)有多余的數(shù)據(jù),但我們可以構(gòu)造一個(gè)例子來(lái)展示其效果。假設(shè)我們有一個(gè) projects 表,它關(guān)聯(lián)到 departments 表。

-- 創(chuàng)建項(xiàng)目表 (假設(shè)項(xiàng)目屬于部門(mén))
CREATE TABLE projects (
    project_id SERIAL PRIMARY KEY,
    project_name VARCHAR(100) NOT NULL,
    dept_id INT, -- 外鍵
    budget DECIMAL(12, 2)
);
-- 插入項(xiàng)目數(shù)據(jù)
INSERT INTO projects (project_name, dept_id, budget) VALUES
('Website Redesign', 1, 50000.00),
('Mobile App', 2, 100000.00),
('Market Research', 3, 25000.00),
('New Office Setup', 5, 75000.00); -- 部門(mén)ID 5 在 departments 表中不存在
-- 查詢(xún)項(xiàng)目及其所屬部門(mén) (使用 RIGHT JOIN)
SELECT p.project_name, p.budget, d.dept_name
FROM projects p
RIGHT JOIN departments d ON p.dept_id = d.dept_id
ORDER BY d.dept_name;

解釋:

  • FROM projects p: 主表是 projects。
  • RIGHT JOIN departments d: 連接 departments 表。
  • ON p.dept_id = d.dept_id: 連接條件。
  • ORDER BY d.dept_name: 按部門(mén)名稱(chēng)排序。

執(zhí)行結(jié)果:

project_namebudgetdept_name
Website Redesign50000.00Human Resources
Mobile App100000.00Engineering
Market Research25000.00Marketing
NULLNULLFinance

注意: “Finance” 部門(mén)在 projects 表中沒(méi)有對(duì)應(yīng)的項(xiàng)目,因此它的 project_namebudget 字段顯示為 NULL。這體現(xiàn)了 RIGHT JOIN 的特點(diǎn):保留右表的所有記錄。

五、全外連接 (FULL OUTER JOIN) 

5.1 全外連接的工作原理

全外連接(FULL OUTER JOIN)返回左表和右表中的所有行。對(duì)于左表中沒(méi)有匹配項(xiàng)的行,右表的字段填充為 NULL;對(duì)于右表中沒(méi)有匹配項(xiàng)的行,左表的字段填充為 NULL。

5.2 FULL JOIN 查詢(xún)示例

讓我們?cè)俅问褂?employeesdepartments 表來(lái)演示 FULL JOIN:

SELECT e.emp_name, e.salary, d.dept_name
FROM employees e
FULL OUTER JOIN departments d ON e.dept_id = d.dept_id
ORDER BY e.emp_name, d.dept_name;

解釋:

  • FULL OUTER JOIN departments d: 使用 FULL OUTER JOIN 連接部門(mén)表。

執(zhí)行結(jié)果:

emp_namesalarydept_name
Alice Johnson75000.00Human Resources
Bob Smith85000.00Engineering
Carol Davis90000.00Engineering
David Wilson65000.00Marketing
Eve Brown70000.00Human Resources
Frank Miller80000.00Finance
Grace Lee55000.00NULL
NULLNULLFinance

注意:

  • 所有員工(包括 “Grace Lee”)和所有部門(mén)(包括 “Finance”)都被包含在結(jié)果中。
  • “Grace Lee” 的部門(mén)信息為 NULL
  • “Finance” 部門(mén)的員工信息為 NULL。

5.3 實(shí)際應(yīng)用場(chǎng)景

FULL JOIN 適用于需要全面了解兩個(gè)表中所有數(shù)據(jù)的情況,尤其是在進(jìn)行數(shù)據(jù)比較或?qū)徲?jì)時(shí)。

六、交叉連接 (CROSS JOIN) 

6.1 交叉連接的工作原理

交叉連接(CROSS JOIN)也稱(chēng)為笛卡爾積(Cartesian Product)。它返回第一個(gè)表中的每一行與第二個(gè)表中的每一行的組合。結(jié)果集的行數(shù)等于第一個(gè)表的行數(shù)乘以第二個(gè)表的行數(shù)。這種連接通常在沒(méi)有 ON 子句時(shí)發(fā)生。

6.2 CROSS JOIN 查詢(xún)示例

讓我們用 employeesdepartments 表來(lái)演示交叉連接的效果:

SELECT e.emp_name, d.dept_name
FROM employees e
CROSS JOIN departments d
ORDER BY e.emp_name, d.dept_name;

解釋:

  • CROSS JOIN departments d: 執(zhí)行交叉連接。
  • 由于沒(méi)有 ON 子句,結(jié)果將是所有員工與所有部門(mén)的組合。

執(zhí)行結(jié)果:

emp_namedept_name
Alice JohnsonFinance
Alice JohnsonHuman Resources
Alice JohnsonMarketing
Alice JohnsonEngineering
Bob SmithFinance
Bob SmithHuman Resources
Bob SmithMarketing
Bob SmithEngineering
Carol DavisFinance
Carol DavisHuman Resources
Carol DavisMarketing
Carol DavisEngineering
David WilsonFinance
David WilsonHuman Resources
David WilsonMarketing
David WilsonEngineering
Eve BrownFinance
Eve BrownHuman Resources
Eve BrownMarketing
Eve BrownEngineering
Frank MillerFinance
Frank MillerHuman Resources
Frank MillerMarketing
Frank MillerEngineering
Grace LeeFinance
Grace LeeHuman Resources
Grace LeeMarketing
Grace LeeEngineering

注意: 結(jié)果包含 7 個(gè)員工 × 4 個(gè)部門(mén) = 28 行。這是典型的笛卡爾積,每一行代表一個(gè)員工和一個(gè)部門(mén)的組合。

6.3 實(shí)際應(yīng)用場(chǎng)景

交叉連接在以下場(chǎng)景中有用:

  • 生成測(cè)試數(shù)據(jù): 生成所有可能的組合來(lái)測(cè)試程序。
  • 計(jì)算組合: 例如,生成所有可能的顏色和尺寸搭配。
  • 特殊情況: 在某些需要所有組合的業(yè)務(wù)邏輯中。

七、Java 與 PostgreSQL 的集成:實(shí)戰(zhàn)演練 

為了將理論知識(shí)轉(zhuǎn)化為實(shí)踐,我們將在 Java 應(yīng)用程序中使用 JDBC 來(lái)連接 PostgreSQL 數(shù)據(jù)庫(kù),并執(zhí)行各種類(lèi)型的 JOIN 查詢(xún)。

7.1 環(huán)境準(zhǔn)備

在開(kāi)始編碼之前,請(qǐng)確保你已經(jīng):

  1. 安裝并運(yùn)行了 PostgreSQL 數(shù)據(jù)庫(kù)。
  2. 創(chuàng)建了我們上面提到的 employeesdepartments 表,并插入了示例數(shù)據(jù)。
  3. 在你的 Java 項(xiàng)目中添加了 PostgreSQL JDBC 驅(qū)動(dòng)依賴(lài)。如果你使用 Maven,可以在 pom.xml 中添加以下依賴(lài)項(xiàng):
<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>42.6.0</version> <!-- 請(qǐng)檢查最新版本 -->
</dependency>

7.2 基礎(chǔ)連接配置

首先,我們需要一個(gè)簡(jiǎn)單的工具類(lèi)來(lái)管理數(shù)據(jù)庫(kù)連接。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DatabaseConnection {
    private static final String URL = "jdbc:postgresql://localhost:5432/your_database_name"; // 替換為你的數(shù)據(jù)庫(kù)名
    private static final String USER = "your_username"; // 替換為你的用戶(hù)名
    private static final String PASSWORD = "your_password"; // 替換為你的密碼
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(URL, USER, PASSWORD);
    }
}

7.3 示例 1:內(nèi)連接 (INNER JOIN) 查詢(xún)員工及其部門(mén)

我們將編寫(xiě)一個(gè) Java 方法來(lái)執(zhí)行 INNER JOIN 查詢(xún)。

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

// 用于存儲(chǔ)查詢(xún)結(jié)果的簡(jiǎn)單類(lèi)
class EmployeeWithDept {
    private String employeeName;
    private Double salary;
    private String departmentName;

    public EmployeeWithDept(String employeeName, Double salary, String departmentName) {
        this.employeeName = employeeName;
        this.salary = salary;
        this.departmentName = departmentName;
    }

    // Getters and Setters
    public String getEmployeeName() { return employeeName; }
    public void setEmployeeName(String employeeName) { this.employeeName = employeeName; }
    public Double getSalary() { return salary; }
    public void setSalary(Double salary) { this.salary = salary; }
    public String getDepartmentName() { return departmentName; }
    public void setDepartmentName(String departmentName) { this.departmentName = departmentName; }

    @Override
    public String toString() {
        return "EmployeeWithDept{" +
                "employeeName='" + employeeName + '\'' +
                ", salary=" + salary +
                ", departmentName='" + departmentName + '\'' +
                '}';
    }
}

public class EmployeeReportService {

    public List<EmployeeWithDept> getEmployeesWithDepartments() throws SQLException {
        List<EmployeeWithDept> results = new ArrayList<>();
        String sql = """
            SELECT e.emp_name, e.salary, d.dept_name
            FROM employees e
            INNER JOIN departments d ON e.dept_id = d.dept_id
            ORDER BY e.emp_name
            """;

        try (Connection conn = DatabaseConnection.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql);
             ResultSet rs = pstmt.executeQuery()) {

            while (rs.next()) {
                String empName = rs.getString("emp_name");
                Double salary = rs.getDouble("salary");
                String deptName = rs.getString("dept_name");
                results.add(new EmployeeWithDept(empName, salary, deptName));
            }
        }
        return results;
    }

    public static void main(String[] args) {
        EmployeeReportService service = new EmployeeReportService();
        try {
            List<EmployeeWithDept> employees = service.getEmployeesWithDepartments();
            System.out.println("=== 員工及其部門(mén) (INNER JOIN) ===");
            for (EmployeeWithDept emp : employees) {
                System.out.println(emp);
            }
        } catch (SQLException e) {
            e.printStackTrace(); // 在實(shí)際應(yīng)用中,應(yīng)該使用更健壯的日志記錄
        }
    }
}

代碼解析:

  1. EmployeeWithDept 類(lèi): 定義了一個(gè)簡(jiǎn)單的數(shù)據(jù)傳輸對(duì)象 (DTO),用于封裝從數(shù)據(jù)庫(kù)查詢(xún)得到的員工姓名、薪資和部門(mén)名稱(chēng)。
  2. getEmployeesWithDepartments() 方法:
    • 構(gòu)建 SQL 查詢(xún)字符串,使用了 Java 15+ 的文本塊 (Text Block) 語(yǔ)法。
    • 使用 try-with-resources 語(yǔ)句自動(dòng)管理數(shù)據(jù)庫(kù)資源。
    • PreparedStatement 用于執(zhí)行預(yù)編譯的 SQL 語(yǔ)句。
    • executeQuery() 執(zhí)行查詢(xún)并返回 ResultSet
    • ResultSet.next() 遍歷結(jié)果集。
    • getString()getDouble()ResultSet 中獲取對(duì)應(yīng)列的值。
    • 將結(jié)果封裝成 EmployeeWithDept 對(duì)象并添加到列表中。
  3. main() 方法: 創(chuàng)建服務(wù)實(shí)例并調(diào)用 getEmployeesWithDepartments() 方法,打印查詢(xún)結(jié)果。

預(yù)期輸出:

=== 員工及其部門(mén) (INNER JOIN) ===
EmployeeWithDept{employeeName='Alice Johnson', salary=75000.0, departmentName='Human Resources'}
EmployeeWithDept{employeeName='Bob Smith', salary=85000.0, departmentName='Engineering'}
EmployeeWithDept{employeeName='Carol Davis', salary=90000.0, departmentName='Engineering'}
EmployeeWithDept{employeeName='David Wilson', salary=65000.0, departmentName='Marketing'}
EmployeeWithDept{employeeName='Eve Brown', salary=70000.0, departmentName='Human Resources'}
EmployeeWithDept{employeeName='Frank Miller', salary=80000.0, departmentName='Finance'}

7.4 示例 2:左外連接 (LEFT JOIN) 查詢(xún)所有員工

現(xiàn)在,我們實(shí)現(xiàn)一個(gè)查詢(xún),獲取所有員工及其部門(mén)信息,包括沒(méi)有部門(mén)的員工。

public class EmployeeReportService {
    // ... (前面的方法)
    public List<EmployeeWithDept> getAllEmployeesWithDepartments() throws SQLException {
        List<EmployeeWithDept> results = new ArrayList<>();
        String sql = """
            SELECT e.emp_name, e.salary, d.dept_name
            FROM employees e
            LEFT JOIN departments d ON e.dept_id = d.dept_id
            ORDER BY e.emp_name
            """;
        try (Connection conn = DatabaseConnection.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql);
             ResultSet rs = pstmt.executeQuery()) {
            while (rs.next()) {
                String empName = rs.getString("emp_name");
                Double salary = rs.getDouble("salary");
                String deptName = rs.getString("dept_name");
                results.add(new EmployeeWithDept(empName, salary, deptName));
            }
        }
        return results;
    }
    public static void main(String[] args) {
        EmployeeReportService service = new EmployeeReportService();
        try {
            List<EmployeeWithDept> employees = service.getAllEmployeesWithDepartments();
            System.out.println("=== 所有員工及其部門(mén) (LEFT JOIN) ===");
            for (EmployeeWithDept emp : employees) {
                System.out.println(emp);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

代碼解析:

  1. SQL 中的 LEFT JOIN: 使用 LEFT JOIN 替代 INNER JOIN。
  2. getAllEmployeesWithDepartments() 方法: 執(zhí)行 LEFT JOIN 查詢(xún)。

預(yù)期輸出:

=== 所有員工及其部門(mén) (LEFT JOIN) ===
EmployeeWithDept{employeeName='Alice Johnson', salary=75000.0, departmentName='Human Resources'}
EmployeeWithDept{employeeName='Bob Smith', salary=85000.0, departmentName='Engineering'}
EmployeeWithDept{employeeName='Carol Davis', salary=90000.0, departmentName='Engineering'}
EmployeeWithDept{employeeName='David Wilson', salary=65000.0, departmentName='Marketing'}
EmployeeWithDept{employeeName='Eve Brown', salary=70000.0, departmentName='Human Resources'}
EmployeeWithDept{employeeName='Frank Miller', salary=80000.0, departmentName='Finance'}
EmployeeWithDept{employeeName='Grace Lee', salary=55000.0, departmentName='null'}

7.5 示例 3:使用參數(shù)化查詢(xún)進(jìn)行動(dòng)態(tài) JOIN

讓我們編寫(xiě)一個(gè)方法,根據(jù)部門(mén) ID 獲取特定部門(mén)的員工信息。

public class EmployeeReportService {
    // ... (前面的方法)
    public List<EmployeeWithDept> getEmployeesByDepartmentId(int deptId) throws SQLException {
        List<EmployeeWithDept> results = new ArrayList<>();
        String sql = """
            SELECT e.emp_name, e.salary, d.dept_name
            FROM employees e
            INNER JOIN departments d ON e.dept_id = d.dept_id
            WHERE d.dept_id = ?
            ORDER BY e.emp_name
            """;
        try (Connection conn = DatabaseConnection.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setInt(1, deptId); // 設(shè)置參數(shù) ? 為傳入的部門(mén) ID
            try (ResultSet rs = pstmt.executeQuery()) {
                while (rs.next()) {
                    String empName = rs.getString("emp_name");
                    Double salary = rs.getDouble("salary");
                    String deptName = rs.getString("dept_name");
                    results.add(new EmployeeWithDept(empName, salary, deptName));
                }
            }
        }
        return results;
    }
    public static void main(String[] args) {
        EmployeeReportService service = new EmployeeReportService();
        try {
            // 查詢(xún) Engineering 部門(mén)的員工 (假設(shè) dept_id = 2)
            List<EmployeeWithDept> engineeringEmployees = service.getEmployeesByDepartmentId(2);
            System.out.println("=== Engineering 部門(mén)員工 ===");
            for (EmployeeWithDept emp : engineeringEmployees) {
                System.out.println(emp);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

代碼解析:

  1. SQL 中的參數(shù)化查詢(xún): 使用 ? 作為占位符。
  2. 設(shè)置參數(shù): pstmt.setInt(1, deptId)? 替換為傳入的 deptId 值。
  3. getEmployeesByDepartmentId() 方法: 接受一個(gè) int 類(lèi)型的參數(shù) deptId,并執(zhí)行帶 WHERE 條件的 INNER JOIN 查詢(xún)。

預(yù)期輸出:

=== Engineering 部門(mén)員工 ===
EmployeeWithDept{employeeName='Bob Smith', salary=85000.0, departmentName='Engineering'}
EmployeeWithDept{employeeName='Carol Davis', salary=90000.0, departmentName='Engineering'}

八、高級(jí)技巧與最佳實(shí)踐 

8.1 JOIN 與 WHERE 的順序

在 SQL 查詢(xún)中,WHERE 子句通常在 JOIN 之后執(zhí)行。這意味著 WHERE 中的條件會(huì)應(yīng)用于已連接后的結(jié)果集。這有助于進(jìn)一步過(guò)濾數(shù)據(jù)。

SELECT e.emp_name, d.dept_name, e.salary
FROM employees e
INNER JOIN departments d ON e.dept_id = d.dept_id
WHERE e.salary > 75000;

這個(gè)查詢(xún)首先執(zhí)行 INNER JOIN,然后在結(jié)果集中篩選出薪資大于 75000 的員工。

8.2 多表 JOIN

JOIN 操作可以擴(kuò)展到多個(gè)表。例如,如果還有 projects 表和 project_assignments 表,可以進(jìn)行三表 JOIN:

SELECT e.emp_name, d.dept_name, p.project_name
FROM employees e
INNER JOIN departments d ON e.dept_id = d.dept_id
INNER JOIN project_assignments pa ON e.emp_id = pa.emp_id
INNER JOIN projects p ON pa.project_id = p.project_id;

8.3 使用別名簡(jiǎn)化查詢(xún)

為表使用別名可以簡(jiǎn)化復(fù)雜的 JOIN 查詢(xún)。

SELECT e.emp_name, d.dept_name
FROM employees e
INNER JOIN departments d ON e.dept_id = d.dept_id;

8.4 性能優(yōu)化建議

  • 索引: 在 JOIN 的列(通常是外鍵)上建立索引,可以顯著提高 JOIN 的性能。
  • 選擇合適的 JOIN 類(lèi)型: 根據(jù)業(yè)務(wù)需求選擇正確的 JOIN 類(lèi)型,避免不必要的數(shù)據(jù)加載。
  • **避免 SELECT ***: 明確指定所需的列,而不是使用 SELECT *,以減少網(wǎng)絡(luò)傳輸和內(nèi)存消耗。
  • 使用 EXPLAIN ANALYZE: PostgreSQL 提供了 EXPLAIN ANALYZE 命令來(lái)分析查詢(xún)計(jì)劃,幫助優(yōu)化慢查詢(xún)。

九、常見(jiàn)誤區(qū)與注意事項(xiàng) 

9.1 忘記在 WHERE 中使用表別名

在復(fù)雜的 JOIN 查詢(xún)中,容易忘記在 WHERE 子句中使用正確的表別名。

-- ? 錯(cuò)誤示例
SELECT e.emp_name, d.dept_name
FROM employees e
INNER JOIN departments d ON e.dept_id = d.dept_id
WHERE dept_id = 1; -- 錯(cuò)誤!應(yīng)為 e.dept_id 或 d.dept_id
-- ? 正確示例
SELECT e.emp_name, d.dept_name
FROM employees e
INNER JOIN departments d ON e.dept_id = d.dept_id
WHERE e.dept_id = 1;

9.2 JOIN 與 WHERE 的混淆

區(qū)分 WHEREJOIN 的作用域很重要。WHERE 用于過(guò)濾最終結(jié)果,而 JOIN 用于定義如何組合表。

9.3 性能陷阱:JOIN 大表

當(dāng)連接兩個(gè)或多個(gè)大型表時(shí),JOIN 操作可能非常耗時(shí)。確保有適當(dāng)?shù)乃饕⒖紤]使用分區(qū)或其他優(yōu)化策略。

十、總結(jié)與展望 

JOIN 查詢(xún)是 PostgreSQL 中最強(qiáng)大的特性之一,它使我們能夠從多個(gè)表中提取和整合數(shù)據(jù)。從簡(jiǎn)單的 INNER JOIN 到復(fù)雜的多表連接,掌握這些技術(shù)對(duì)于構(gòu)建高效、準(zhǔn)確的數(shù)據(jù)庫(kù)應(yīng)用至關(guān)重要。

在 Java 應(yīng)用程序中,通過(guò) JDBC 連接 PostgreSQL 并執(zhí)行 JOIN 查詢(xún),可以構(gòu)建出功能豐富的數(shù)據(jù)驅(qū)動(dòng)系統(tǒng)。無(wú)論是簡(jiǎn)單的員工信息查詢(xún),還是復(fù)雜的業(yè)務(wù)報(bào)表生成,JOIN 都是我們不可或缺的工具。

隨著數(shù)據(jù)量的增長(zhǎng)和分析需求的復(fù)雜化,學(xué)習(xí)更多高級(jí)的 SQL 技巧,如子查詢(xún)、窗口函數(shù)、CTE(公用表表達(dá)式)等,將進(jìn)一步提升你的數(shù)據(jù)處理能力。未來(lái),我們可能會(huì)看到更多與機(jī)器學(xué)習(xí)、實(shí)時(shí)分析等技術(shù)結(jié)合的數(shù)據(jù)庫(kù)解決方案,但掌握這些基礎(chǔ)查詢(xún)技能仍然是理解和利用這些先進(jìn)技術(shù)的基礎(chǔ)。

希望這篇博客能幫助你更好地理解和應(yīng)用 PostgreSQL 的 JOIN 查詢(xún)功能。如果你有任何問(wèn)題或想要了解更高級(jí)的用法,歡迎留言討論!??

參考鏈接:

Mermaid 圖表:JOIN 類(lèi)型比較

Mermaid 圖表:JOIN 查詢(xún)流程

Mermaid 圖表:不同 JOIN 類(lèi)型示意圖

到此這篇關(guān)于PostgreSQL JOIN 聯(lián)表查詢(xún)實(shí)戰(zhàn)演練(內(nèi)連接 / 外連接 / 交叉連接)的文章就介紹到這了,更多相關(guān)PostgreSQL JOIN 聯(lián)表查詢(xún)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Postgresql 如何清理WAL日志

    Postgresql 如何清理WAL日志

    這篇文章主要介紹了Postgresql 實(shí)現(xiàn)清理WAL日志的方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-01-01
  • PostgreSQL數(shù)據(jù)庫(kù)中窗口函數(shù)的語(yǔ)法與使用

    PostgreSQL數(shù)據(jù)庫(kù)中窗口函數(shù)的語(yǔ)法與使用

    這PostgreSQL中提供了窗口函數(shù),一個(gè)窗口函數(shù)在一系列與當(dāng)前行有某種關(guān)聯(lián)的表行上進(jìn)行一種計(jì)算。下面這篇文章主要給大家介紹了關(guān)于PostgreSQL數(shù)據(jù)庫(kù)中窗口函數(shù)的語(yǔ)法與使用的相關(guān)資料,需要的朋友可以參考下
    2019-03-03
  • SpringBoot3集成PostgreSQL的詳細(xì)過(guò)程

    SpringBoot3集成PostgreSQL的詳細(xì)過(guò)程

    PostgreSQL是一個(gè)功能強(qiáng)大的開(kāi)源數(shù)據(jù)庫(kù)系統(tǒng),具有可靠性、穩(wěn)定性、數(shù)據(jù)一致性等特點(diǎn),且可以運(yùn)行在所有主流操作系統(tǒng)上,包括Linux、Unix、Windows等,這篇文章主要介紹了SpringBoot3集成PostgreSQL,需要的朋友可以參考下
    2024-03-03
  • postgresql 中的to_char()常用操作

    postgresql 中的to_char()常用操作

    這篇文章主要介紹了postgresql 中的to_char()常用操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-02-02
  • PostgreSQL中常用的時(shí)間日期腳本使用教程

    PostgreSQL中常用的時(shí)間日期腳本使用教程

    PostgreSQL是一款簡(jiǎn)介而又性能強(qiáng)大的數(shù)據(jù)庫(kù)應(yīng)用程序,其在日期時(shí)間數(shù)據(jù)方面所支持的功能也都非常給力,下面就來(lái)看一下PostgreSQL中常用的日期時(shí)間腳本使用教程.
    2016-05-05
  • PostgreSQL中擴(kuò)展moddatetime的使用

    PostgreSQL中擴(kuò)展moddatetime的使用

    PostgreSQL的moddatetime擴(kuò)展通過(guò)觸發(fā)器自動(dòng)維護(hù)時(shí)間戳字段,輕量高效,適用于審計(jì)日志和多租戶(hù)系統(tǒng),具有一定的參考價(jià)值,感興趣的可以了解一下
    2025-06-06
  • PostgreSQL數(shù)據(jù)庫(kù)備份還原全攻略

    PostgreSQL數(shù)據(jù)庫(kù)備份還原全攻略

    本文主要介紹邏輯備份和連續(xù)歸檔方式的備份及還原,文件系統(tǒng)級(jí)備份由于比較簡(jiǎn)單,這里不在贅述,以下操作使用的數(shù)據(jù)庫(kù)版本為PostgreSQL 12.5,不同的數(shù)據(jù)庫(kù)版本在進(jìn)行連續(xù)歸檔操作時(shí)會(huì)有細(xì)微差異,需要的朋友可以參考下
    2024-04-04
  • postgresql 中的 like 查詢(xún)優(yōu)化方案

    postgresql 中的 like 查詢(xún)優(yōu)化方案

    這篇文章主要介紹了postgresql 中的 like 查詢(xún)優(yōu)化方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-01-01
  • CentOS 9 Stream 上安裝 PostgreSQL 16的步驟

    CentOS 9 Stream 上安裝 PostgreSQL 16的步

    在CentOS9Stream上安裝PostgreSQL16,首先添加PostgreSQL官方倉(cāng)庫(kù),然后禁用系統(tǒng)自帶PostgreSQL版本,避免沖突,使用dnf命令安裝PostgreSQL16,并初始化數(shù)據(jù)庫(kù),本文給大家介紹CentOS 9 Stream 上安裝 PostgreSQL 16的步驟,感興趣的朋友一起看看吧
    2024-11-11
  • PostgreSQL使用MySQL外表的步驟詳解(mysql_fdw)

    PostgreSQL使用MySQL外表的步驟詳解(mysql_fdw)

    這篇文章主要介紹了PostgreSQL使用MySQL外表的步驟(mysql_fdw),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-01-01

最新評(píng)論

麻栗坡县| 女性| 南澳县| 神农架林区| 祁阳县| 凌云县| 临夏市| 望奎县| 大港区| 大同市| 宝坻区| 增城市| 慈溪市| 威远县| 莲花县| 阿瓦提县| 博乐市| 龙川县| 海林市| 江孜县| 丰顺县| 克什克腾旗| 朔州市| 远安县| 中方县| 荥经县| 子洲县| 湾仔区| 杨浦区| 昌邑市| 武山县| 晋中市| 新泰市| 胶州市| 南丹县| 松江区| 曲松县| 师宗县| 普定县| 濮阳市| 东明县|