### **详细用法说明**

#### **1. `FROM` & `JOIN` 子句：构建你的工作台**

* **概念优先**：在查询任何数据之前，你必须先告诉数据库：“我要从哪个（些）表中获取数据？” `FROM`子句就是用来指定你的主数据来源的。当数据分散在多个表中时，就需要用`JOIN`（连接）将它们像拼图一样组合起来，形成一个临时的、更大的数据集合。

* **`FROM` 的基本用法**:

  ```sql
  -- 从名为 'employees' 的表中选取所有数据
  SELECT * FROM employees;
  ```

* **`JOIN` 的艺术（重点）**:

    * **`INNER JOIN` (内连接)**: 只返回两个表中能通过连接条件匹配上的行。这是最常用的连接类型。
      ```sql
      -- 查询每个员工及其所在的部门名称
      SELECT employees.name, departments.dept_name
      FROM employees
      INNER JOIN departments ON employees.department_id = departments.id;
      ```
    * **`LEFT JOIN` (左连接)**: 返回左表的所有行，即使在右表中没有匹配的行。对于右表中不匹配的行，其对应的列将显示为`NULL`。
      ```sql
      -- 查询所有员工以及他们的部门，即使某个员工没有分配部门
      SELECT employees.name, departments.dept_name
      FROM employees
      LEFT JOIN departments ON employees.department_id = departments.id;
      ```
    * **`RIGHT JOIN` (右连接)**: 与`LEFT JOIN`相反，返回右表的所有行。
    * **`FULL OUTER JOIN` (全外连接)**: 返回左表和右表中的所有行。如果某行在一侧没有匹配，另一侧的列将为`NULL`。（注意：SQLite默认不支持`RIGHT JOIN`和`FULL OUTER JOIN`，但可以通过其他方式模拟）。
    * **`CROSS JOIN` (交叉连接)**: 返回左表与右表所有行的笛卡尔积，结果行数是两个表行数的乘积。应谨慎使用。

#### **2. `WHERE` 子句：精确过滤**

* **概念优先**：有了包含所有可能数据的“大工作台”后，`WHERE`子句就像一个筛子，帮你过滤掉不符合条件的行，只留下你真正关心的数据。

* **代码驱动**:

  ```sql
  -- 查询薪水高于50000且在'Sales'部门的员工
  SELECT name, salary
  FROM employees
  WHERE salary > 50000 AND department_id = (SELECT id FROM departments WHERE dept_name = 'Sales');
  ```

    * **最佳实践**: 尽量在`WHERE`子句中过滤掉尽可能多的数据。这能极大减轻后续步骤（如分组、排序）的计算压力。索引在`WHERE`子句的字段上能极大地提升查询性能。

#### **3. `GROUP BY` 子句：聚合数据**

* **概念优先**：当你需要对数据进行汇总统计时，例如“计算每个部门的平均薪水”，`GROUP BY`就能派上用场。它会将具有相同值的行合并成一组，以便对每一组进行聚合计算。

* **代码驱动**:

  ```sql
  -- 计算每个部门的员工数量和平均薪水
  SELECT
      department_id,
      COUNT(*) AS number_of_employees,
      AVG(salary) AS average_salary
  FROM employees
  GROUP BY department_id;
  ```

    * **常见陷阱**: `SELECT`列表中出现的非聚合函数列，都必须出现在`GROUP BY`子句中。

#### **4. `HAVING` 子句：过滤分组**

* **概念优先**：如果说`WHERE`是用来过滤单行的，那么`HAVING`就是用来过滤`GROUP BY`之后形成的分组的。`HAVING`的判断条件通常是聚合函数。

* **代码驱动**:

  ```sql
  -- 找出平均薪水超过60000的部门
  SELECT
      department_id,
      AVG(salary) AS average_salary
  FROM employees
  GROUP BY department_id
  HAVING AVG(salary) > 60000;
  ```

    * **核心区别**: `WHERE`在`GROUP BY`之前执行，作用于行；`HAVING`在`GROUP BY`之后执行，作用于分组。

#### **5. `SELECT` 子句：定义最终输出**

* **概念优先**：这是“流水线”的收尾阶段。在数据经过了筛选、分组和聚合之后，`SELECT`子句最终决定了哪些列或者计算结果将出现在最终的输出中。

* **代码驱动**:

  ```sql
  -- 从员工表中选取员工ID，并创建一个形如 'LastName, FirstName' 的全名列
  SELECT
      id,
      last_name || ', ' || first_name AS full_name,
      salary * 1.1 AS increased_salary -- 计算加薪10%后的薪水
  FROM employees;
  ```

    * **最佳实践**: 明确指定你需要的列，避免使用`SELECT *`。这不仅能提高查询的可读性和性能，还能防止在表结构变更时出现意想不到的问题。

#### **6. `DISTINCT` 关键字：消除重复**

* **概念优先**：`DISTINCT`用于移除结果集中的重复行，确保每一行都是唯一的。

* **代码驱动**:

  ```sql
  -- 查询公司中有哪些不同的职位
  SELECT DISTINCT job_title FROM employees;
  ```

#### **7. `ORDER BY` 子句：结果排序**

* **概念优先**：`ORDER BY`用于对最终的结果集进行排序，可以是升序（`ASC`，默认）或降序（`DESC`）。

* **代码驱动**:

  ```sql
  -- 按薪水降序、姓名升序排列员工信息
  SELECT name, salary
  FROM employees
  ORDER BY salary DESC, name ASC;
  ```

#### **8. `LIMIT` / `OFFSET` 子句：分页展示**

* **概念优先**：当结果集非常大时，通常需要分页显示。`LIMIT`指定最多返回多少行，`OFFSET`指定从第几行开始返回。

* **代码驱动**:

  ```sql
  -- 查询薪水排名第11到第20的员工
  SELECT name, salary
  FROM employees
  ORDER BY salary DESC
  LIMIT 10 OFFSET 10; -- 跳过前10行，然后取10行
  ```

### **高级查询技巧**

除了上述基本子句，`SELECT`语句的强大之处还体现在其对复杂查询的构建能力上，这些技巧在`agi_sql_study.md`中被视为核心知识：

* **子查询 (Subqueries)**: 一个查询嵌套在另一个查询内部。它可以出现在`WHERE`, `FROM`, `SELECT`等多个子句中，但过多或过于复杂的子查询可能影响性能。

* **通用表表达式 (CTEs - Common Table Expressions)**: 使用`WITH`子句可以定义一个或多个临时的、命名的结果集，让复杂的查询逻辑更清晰、可读性更高。

  ```sql
  WITH DepartmentSalaries AS (
      SELECT
          department_id,
          AVG(salary) AS avg_dept_salary
      FROM employees
      GROUP BY department_id
  )
  SELECT e.name, e.salary, ds.avg_dept_salary
  FROM employees e
  JOIN DepartmentSalaries ds ON e.department_id = ds.department_id
  WHERE e.salary > ds.avg_dept_salary;
  ```

* **集合运算 (Set Operations)**: `UNION`, `UNION ALL`, `INTERSECT`, `EXCEPT` 用于合并多个`SELECT`语句的结果集。

* **窗口函数 (Window Functions)**: 这是数据分析的“秘密武器”。它允许你在与当前行相关的“窗口”（一组行）上执行计算，而无需使用`GROUP BY`。可以轻松解决排名、累计求和、移动平均等复杂问题。

  ```sql
  -- 查询每个员工的薪水，以及其所在部门的平均薪水
  SELECT
      name,
      salary,
      AVG(salary) OVER (PARTITION BY department_id) AS avg_dept_salary
  FROM employees;
  ```

希望这份详尽的指南能帮助你更深刻地理解`SELECT`语句。请记住，写SQL不仅是技术的展现，更是逻辑思维的体现。不断练习，并始终思考查询的逻辑执行顺序，你终将成为一名真正的SQL大师。