← MySQL 读取数据 MySQL 删除表 →

Node.js mysql2去重查询:SELECT DISTINCT单列、多列组合、WHERE过滤与参数化教程

著
原创 2026-10-13 Node.js 已有人查阅

去重这件事,SQL层面就能做完

做列表筛选、下拉选项、报表分组时,经常遇到一个需求:某个字段的取值只需要出现一次。比如前端要渲染一个部门下拉框,数据源是员工表,同一个部门有几十号人,直接查department列会返回几十条重复值,交给JavaScript用Set去重当然也能做,但数据已经从MySQL传到Node.js再过滤,网络传输和内存都白花了。

SELECT DISTINCT把去重放在数据库侧完成,返回的就是已经去重的结果集。这一节围绕Node.js+mysql2执行SELECT DISTINCT展开,把单列、多列组合、条件过滤和参数化写法讲清楚。

DISTINCT到底做了什么

DISTINCT是SELECT的一个关键字,作用是去掉查询结果中的重复行。它作用在结果集上,不改动表里的数据。表里有多少条重复记录,执行完之后还是那么多条,只是返回给你的结果里每个值只出现一次。

环境准备

动手前确认:

  • 系统已安装Node.js

  • MySQLServer已安装并处于运行状态

  • 项目里已安装mysql2包

  • 目标数据库和Employees表已存在并有数据

安装mysql2:

npm install mysql2

示例统一使用下面这张Employees表:

employeeId name department city
1 John Sales London
2 Emma HR Manchester
3 Michael Sales London
4 Sophia IT Birmingham
5 Daniel HR London

先看看不去重是什么效果:

SELECT department FROM Employees;

返回:

Sales
HR
Sales
IT
HR

Sales出现两次,HR出现两次。加上DISTINCT:

SELECT DISTINCT department FROM Employees;

返回:

Sales
HR
IT

重复值被去掉了。

SELECT DISTINCT的基本语法

SELECT DISTINCT column_name FROM table_name;

SELECT指定要读取的数据,DISTINCT表示对结果去重,column_name是要读取的列,table_name是目标表。

在Node.js中查询单列唯一值

const mysql = require('mysql2');

const connection = mysql.createConnection({
  host: 'localhost',
  user: 'root',
  password: 'your_password',
  database: 'companydb'
});

const sql = "SELECT DISTINCT department FROM Employees";

connection.query(sql, (err, result) => {
  if (err) {
    console.log("错误:", err.message);
    return;
  }
  console.log(result);
});

connection.end();

返回:

[
  { department: 'Sales' },
  { department: 'HR' },
  { department: 'IT' }
]

每个部门只出现一次。结果依然是数组,数组元素是对象,键为列名。

查询另一列的唯一值

把列换成city,就是城市去重:

const sql = "SELECT DISTINCT city FROM Employees";

connection.query(sql, (err, result) => {
  if (err) {
    console.log("错误:", err.message);
    return;
  }
  console.log(result);
});

返回:

[
  { city: 'London' },
  { city: 'Manchester' },
  { city: 'Birmingham' }
]

London在表里出现了三次,去重后只返回一次。

多列组合去重

DISTINCT后面跟多列时,去重的判断依据是这些列组合起来的值,而不是单列。这一点经常被误解。

const sql = "SELECT DISTINCT department, city FROM Employees";

connection.query(sql, (err, result) => {
  if (err) {
    console.log("错误:", err.message);
    return;
  }
  console.log(result);
});

返回:

[
  { department: 'Sales', city: 'London' },
  { department: 'HR', city: 'Manchester' },
  { department: 'IT', city: 'Birmingham' },
  { department: 'HR', city: 'London' }
]

结果里有两条HR,但对应的city不同,一条是Manchester,一条是London,所以(HR,Manchester)和(HR,London)是两个不同的组合,都保留。Sales和London这个组合在表里出现了两次(John和Michael),去重后只返回一次。

这里有个实际项目里踩过的坑:有人写SELECT DISTINCTdepartment,city,本意是“把部门去重,顺便把城市也带出来”,结果发现部门还是重复的。原因是DISTINCT作用在整行上,不是只作用在第一列。要单列去重就只写那一列。

配合WHERE先过滤再去重

WHERE在DISTINCT之前生效,先把不满足条件的行筛掉,再对剩下的结果去重。

const sql = "SELECT DISTINCT department FROM Employees WHERE city = 'London'";

connection.query(sql, (err, result) => {
  if (err) {
    console.log("错误:", err.message);
    return;
  }
  console.log(result);
});

返回:

[
  { department: 'Sales' },
  { department: 'HR' }
]

逻辑顺序是:先找出city为London的员工(John、Michael、Daniel),取出他们的department(Sales、Sales、HR),再去重得到Sales和HR。

配合ORDERBY排序

DISTINCT返回的顺序不保证稳定,想要按字母或数值排序要显式加ORDERBY。

const sql = "SELECT DISTINCT department FROM Employees ORDER BY department";

connection.query(sql, (err, result) => {
  if (err) {
    console.log("错误:", err.message);
    return;
  }
  console.log(result);
});

返回:

[
  { department: 'HR' },
  { department: 'IT' },
  { department: 'Sales' }
]

这里ORDERBY的字段必须是SELECT里出现的字段,否则MySQL在某些配置下会报错。如果确实需要按没选出来的字段排序,可以用聚合函数或者改写成GROUPBY。

参数化查询处理用户输入

城市名来自前端时,不能拼接字符串:

const sql = "SELECT DISTINCT department FROM Employees WHERE city = ?";

connection.query(sql, [city], (err, result) => {
  if (err) {
    console.log("错误:", err.message);
    return;
  }
  console.log(result);
});

完整一点,用readline模拟用户输入:

const mysql = require('mysql2');
const readline = require('readline');

const connection = mysql.createConnection({
  host: 'localhost',
  user: 'root',
  password: 'your_password',
  database: 'companydb'
});

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

rl.question('请输入城市: ', (city) => {
  const sql = "SELECT DISTINCT department FROM Employees WHERE city = ?";

  connection.query(sql, [city], (err, result) => {
    if (err) {
      console.log("错误:", err.message);
      return;
    }
    console.log(result);
    connection.end();
    rl.close();
  });
});

输入London后返回:

[
  { department: 'Sales' },
  { department: 'HR' }
]

?占位符把用户输入当作纯值处理,即使输入里带引号或SQL关键字,也不会被当作语句的一部分执行。

列名写错时怎么暴露问题

const sql = "SELECT DISTINCT dept FROM Employees";

connection.query(sql, (err, result) => {
  if (err) {
    console.log("错误:", err.message);
    return;
  }
  console.log(result);
});

输出:

错误: Unknown column 'dept' in 'field list'

实际列名是department,写成dept,MySQL直接报字段不存在。回调里判断err并return,后面的代码就不会拿着undefined继续执行。

DISTINCT和GROUPBY的选择

两者都能实现去重,但语义和使用场景有区别。

SELECT DISTINCTdepartmentFROMEmployees和SELECTdepartmentFROMEmployeesGROUPBYdepartment在这个场景下返回结果一样。DISTINCT表达的是“去重”,GROUPBY表达的是“分组”,后者通常还伴随聚合函数,比如SELECTdepartment,COUNT(*)FROMEmployeesGROUPBYdepartment。

单纯去重就用DISTINCT,需要同时统计每组数量就用GROUPBY。执行计划上两者在MySQL里经常被优化成相似的路径,性能差异不大,选哪个更多看语义是否清晰。

关于性能的一点经验

DISTINCT需要对结果做去重,MySQL内部通常会用到临时表或排序。数据量大、去重列没有索引时,开销会比较明显。

如果去重列上有索引,MySQL可以直接利用索引有序性跳过重复值,效率高很多。所以对经常需要DISTINCT的列,考虑加索引。

另一个思路是从数据模型上避免。如果部门是固定的几个值,单独建一张departments表,员工表用外键关联,查询部门列表直接查departments表,根本不需要DISTINCT。这是更根本的解法,只是改造成本高,要看项目阶段决定。

现在写法:Promise与async/await

const mysql = require('mysql2/promise');

async function getUniqueDepartments(city) {
  const connection = await mysql.createConnection({
    host: 'localhost',
    user: 'root',
    password: 'your_password',
    database: 'companydb'
  });

  try {
    const sql = "SELECT DISTINCT department FROM Employees WHERE city = ? ORDER BY department";
    const [rows] = await connection.execute(sql, [city]);
    return rows;
  } catch (err) {
    console.error("查询失败:", err.message);
    throw err;
  } finally {
    await connection.end();
  }
}

解构赋值const[rows]取出结果行数组,第二个元素是字段元信息,一般用不上。finally保证连接释放,异常情况下也不会泄漏。

本节课程知识要点

  • DISTINCT作用在结果集上,不修改表中的数据

  • 多列DISTINCT按列组合去重,不是按第一列去重

  • WHERE在DISTINCT之前生效,先过滤再去重

  • ORDERBY的字段需要在SELECT列表中出现

  • 用户输入走?占位符,避免SQL注入

  • 去重列有索引时性能更好,数据模型合理时可以从源头避免去重需求

  • 单纯去重用DISTINCT,需要聚合统计用GROUPBY

  • mysql2/promise配合async/await更适合现在项目结构

练习思考题

  1. 写一条SELECT DISTINCT语句,查询Employees表中所有不重复的city,并按city字母顺序排序。

  2. 查询department和city的唯一组合,只保留city为London的记录,用参数化查询实现。

  3. 解释SELECT DISTINCT department,city FROM Employees和SELECT DISTINCT depart ment FROM Employees返回结果的区别,并说明原因。

  4. 如果一张表有百万级数据,需要对某个列做DISTINCT查询,从索引和数据模型两个角度说说可以怎么优化。

← MySQL 读取数据 MySQL 删除表 →
分享笔记 (共有 篇笔记)
验证码:
微信公众号