Joyber 发布的文章

在 MySQL 中,除了 IF(A, B, C)CASE 表达式外,还有一个函数可以实现类似的条件判断功能,即 NULLIF(expr1, expr2),不过它的使用场景更具体。

NULLIF(expr1, expr2) 的作用是:

  • expr1 等于 expr2 时,返回 NULL
  • expr1 不等于 expr2 时,返回 expr1

虽然它的逻辑不如 IF 灵活(只能判断是否相等),但可以看作是一种特殊的条件判断。例如:

-- 当 score 等于 100 时返回 NULL,否则返回 score
SELECT NULLIF(score, 100) AS processed_score FROM students;

-- 等效于 IF 函数的写法
SELECT IF(score = 100, NULL, score) AS processed_score FROM students;

另外,还有 COALESCE(expr1, expr2, ...) 函数,它用于返回参数列表中第一个非 NULL 的值,也可以实现简单的条件逻辑:

-- 如果 name 为 NULL,返回 '未知',否则返回 name
SELECT COALESCE(name, '未知') AS user_name FROM users;

-- 等效于 IF 函数的写法
SELECT IF(name IS NULL, '未知', name) AS user_name FROM users;

这两个函数都是 MySQL 中用于特定场景的条件处理函数,虽然功能范围比 IF(A, B, C) 更窄,但在合适的场景下可以替代使用。

COALESCE 函数的支持度

COALESCE 函数是SQL标准函数,在包括 Microsoft SQL Server(MSSQL)在内的主流关系型数据库中都得到了广泛支持,并且功能保持一致:返回参数列表中第一个非 NULL 的值。

在 SQL Server 中,COALESCE 的用法与 MySQL、MariaDB 等完全兼容,例如:

-- SQL Server 中使用 COALESCE
SELECT COALESCE(NULL, '默认值', '备选值') AS result;
-- 返回 '默认值'(第一个非NULL值)

除了 SQL Server,以下主流数据库也均支持 COALESCE 函数:

  • PostgreSQL
  • Oracle
  • DB2
  • SQLite(3.32.0 及以上版本)

这一函数的通用性使其成为跨数据库开发中处理 NULL 值的优选方案,相比数据库特定函数(如 SQL Server 的 ISNULL、MySQL 的 IFNULL),COALESCE 具有更好的兼容性。

在 CSS 中,控制表格的 <th>(表头单元格)和 <td>(数据单元格)之间的间距主要涉及以下几个属性:

  1. border-spacing - 用于设置相邻单元格边框之间的距离(仅在 border-collapse: separate 时有效)

    table {
      border-collapse: separate; /* 默认值 */
      border-spacing: 10px; /* 水平和垂直间距都是10px */
      /* 也可以分别设置水平和垂直间距 */
      border-spacing: 10px 20px; /* 水平10px,垂直20px */
    }
  2. padding - 用于设置单元格内容与边框之间的内边距(单元格内部的间距)

    th, td {
      padding: 8px 12px; /* 上下内边距8px,左右内边距12px */
    }
  3. border-collapse - 控制表格边框的合并方式,会影响间距表现:

    • border-collapse: collapse:边框合并,border-spacing 失效
    • border-collapse: separate:边框分离,border-spacing 生效(默认值)

通常推荐的表格样式组合:

table {
  border-collapse: collapse; /* 合并边框,避免双重边框 */
  width: 100%;
}

th, td {
  padding: 10px 15px; /* 单元格内边距,控制内容与边框的距离 */
  border: 1px solid #ddd; /* 单元格边框 */
}

这种组合既能保证单元格有适当的内部间距(通过 padding),又能避免边框之间出现不必要的空隙。如果需要单元格之间有明确的间隔,可以使用 border-collapse: separate 配合 border-spacing

 $job = query('job',"id='$staff[jobId]'");
                $and .= " and (
                            TB.stid IN (
                SELECT        stid 
                FROM        `staff` 
                WHERE        jobId IN (
                    SELECT  id 
                    FROM  job 
                    WHERE  departmentId IN (
                            WITH RECURSIVE subordinates AS (
                            SELECT id, pid 
                            FROM department 
                            WHERE id = '$job[departmentId]' UNION ALL
                            SELECT e.id, e.pid 
                            FROM department e
                                INNER JOIN subordinates s ON e.pid = s.id 
                            ) SELECT    id 
                        FROM  subordinates 
                        ))) 
                OR manager IN (
                SELECT        stid 
                FROM        `staff` 
                WHERE        jobId IN (
                    SELECT  id 
                    FROM  job 
                    WHERE  departmentId IN (
                            WITH RECURSIVE subordinates AS (
                                SELECT id, pid 
                                FROM department 
                                WHERE id = '$job[departmentId]' UNION ALL
                                SELECT e.id, e.pid 
                                FROM department e
                                INNER JOIN subordinates s ON e.pid = s.id 
                            ) SELECT  id  FROM  subordinates 
                        ) 
                    ) 
                )
               ) ";

这个MySQL语句使用了递归CTE(Common Table Expression)来查询部门及其所有子部门。让我来详细解释:

语句结构分析

1. 递归CTE定义

WITH RECURSIVE subordinates AS (
    -- ...
)
  • 使用 WITH RECURSIVE 创建一个递归的公用表表达式
  • subordinates 是CTE的名称

2. 初始查询(锚点)

SELECT id, pid 
FROM department 
WHERE id = 'adacd6795f3b51d1'
  • 这是递归的起点,选择ID为 'adacd6795f3b51d1' 的部门
  • 获取该部门的 idpid(父级ID)

3. 递归查询部分

UNION ALL
SELECT e.id, e.pid 
FROM department e
    INNER JOIN subordinates s ON e.pid = s.id
  • 使用 UNION ALL 连接初始查询和递归查询
  • 递归地查找所有子部门:

    • department 表中选择记录(别名为 e
    • 通过 INNER JOIN 连接到CTE本身(别名为 s
    • 连接条件是 e.pid = s.id,即查找父ID等于当前CTE中ID的记录

4. 最终查询

SELECT id 
FROM subordinates
  • 从递归CTE结果中选择所有部门的 id

功能说明

这个查询的作用是:

  1. 从指定ID的部门开始('adacd6795f3b51d1')
  2. 递归查找该部门的所有下级子部门(直接和间接的子部门)
  3. 返回包含原始部门及其所有子部门的ID列表

这是一种典型的组织架构查询,用于获取某个部门及其所有下属部门的完整层级结构。

核心部分

WITH RECURSIVE subordinates AS (
                            SELECT id, pid 
                            FROM department 
                            WHERE id = 'a7df92713102e4b6' UNION ALL
                            SELECT e.id, e.pid 
                            FROM department e
                                INNER JOIN subordinates s ON e.pid = s.id 
                            ) SELECT    id 
                        FROM  subordinates 

subordinates 是临时表名,可以替换

简化SQL,分步查询


    /**
     * 获取当前部门及所有子部门的ID
     * @param $departmentId
     * @return array
     */
    public static function getAllChildDepartments($departmentId)
    {
        $sql = "WITH RECURSIVE subordinates AS (
                    SELECT id, pid 
                    FROM department 
                    WHERE id = '$departmentId' UNION ALL
                    SELECT e.id, e.pid 
                    FROM department e
                    INNER JOIN subordinates s ON e.pid = s.id 
                ) SELECT id FROM subordinates";
        $data = self::sqlFind($sql);
        return array_column($data, 'id');
    }

                $job = query('job',"id='$staff[jobId]'");
                //查询该职位所在部门的所有子部门
                $allDepartmentIds = departmentClass::getAllChildDepartments($job['departmentId']);
                $jobs = find([
                    "table" => "job",
                    "where" => "departmentId IN ('".implode("','",$allDepartmentIds)."')",
                    "field" => 'id',
                ]);
                //这些所有子部门的职位
                $jobIds = array_column($jobs['array'],'id');
                //查询这些职位的员工ID
                $staffs = find([
                    "table" => "staff",
                    "where" => "jobId IN ('".implode("','",$jobIds)."')",
                    "field" => 'stid',
                ]);
                $staffIds = array_column($staffs['array'],'stid');
                $and .= " and (TB.stid IN ('".implode("','",$staffIds)."') OR TB.manager IN ('".implode("','",$staffIds)."'))";
           

使用 FFmpeg 合并多个视频有多种方法,具体取决于视频格式和合并需求。以下是几种常见的合并方式及对应的命令:

方法一:使用 concat 协议(最简单,适合相同编码的视频)

如果你的视频具有相同的编码格式(如都是 H.264 + AAC),可以直接使用 concat 协议:

ffmpeg -i "concat:video1.mp4|video2.mp4|video3.mp4" -c copy output.mp4

注意

  • 视频需使用相同编码、分辨率和帧率。
  • 不同平台的分隔符可能不同:

    • Windows 使用 concat:video1.mp4|video2.mp4
    • Linux/macOS 使用 concat:video1.mp4\video2.mp4

方法二:使用 concat 文件列表(推荐,兼容性更好)

步骤 1:创建文本文件 filelist.txt

file 'video1.mp4'
file 'video2.mp4'
file 'video3.mp4'

步骤 2:执行合并命令

ffmpeg -f concat -safe 0 -i filelist.txt -c copy output.mp4

优点

  • 支持不同编码的视频(会重新编码)。
  • 支持文件名包含特殊字符。

方法三:重新编码合并(适合不同编码的视频)

如果视频编码不同,可以通过重新编码统一格式:

ffmpeg -f concat -safe 0 -i filelist.txt -c:v libx264 -c:a aac output.mp4

参数说明

  • -c:v libx264:视频使用 H.264 编码。
  • -c:a aac:音频使用 AAC 编码。

方法四:垂直/水平拼接视频

如果需要将多个视频并排或上下排列,可以使用 hstackvstack 滤镜:

# 水平拼接(并排)
ffmpeg -i video1.mp4 -i video2.mp4 -filter_complex hstack output.mp4

# 垂直拼接(上下)
ffmpeg -i video1.mp4 -i video2.mp4 -filter_complex vstack output.mp4

注意:视频分辨率需一致,否则会自动裁剪。

常见问题及解决方法

  1. 合并后无声:添加 -map 0:a -map 1:a 指定音频流。
  2. 时间戳错误:添加 -fflags +genpts 强制生成时间戳。
  3. 编码不兼容:使用方法三重新编码所有视频。

根据你的具体需求选择合适的方法即可。如果视频格式复杂,建议使用方法二或方法三以确保兼容性。

在 MySQL 8.0 中添加授权用户并允许所有 IP 访问的步骤如下(注意:此配置仅适用于本地开发环境,生产环境切勿开放 root 用户远程访问):

1. 登录 MySQL

mysql -u root -p

输入当前 root 用户密码登录。

2. 创建远程访问用户(使用 IDENTIFIED BY 语法)

CREATE USER 'root'@'%' IDENTIFIED BY '123456';
  • 'root'@'%':创建名为 root 的用户,允许从任何 IP 地址连接
  • IDENTIFIED BY '123456':设置密码为 123456

3. 授予所有权限

GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;
  • ALL PRIVILEGES:授予所有权限(生产环境建议按需分配)
  • *.*:对所有数据库的所有表生效
  • WITH GRANT OPTION:允许该用户将自己拥有的权限授予他人

4. 刷新权限

FLUSH PRIVILEGES;

5. 确认用户权限

SELECT user, host, authentication_string FROM mysql.user WHERE user = 'root';

应看到类似以下输出:

+------+------+-------------------------------------------+
| user | host | authentication_string                     |
+------+------+-------------------------------------------+
| root | %    | $A$005$........(加密后的密码)........... |
+------+------+-------------------------------------------+

6. 修改 MySQL 配置文件(可选)

如果仍然无法远程连接,可能需要修改 MySQL 配置文件 /etc/mysql/mysql.conf.d/mysqld.cnf,注释掉 bind-address

# bind-address = 127.0.0.1

然后重启 MySQL 服务:

sudo systemctl restart mysql

注意事项

  1. 安全风险:此配置会使 root 用户可以从任意 IP 访问,仅适用于本地开发环境
  2. 防火墙:确保服务器防火墙开放 3306 端口
  3. 生产环境建议

    • 创建专用用户而非使用 root
    • 限制访问 IP(如 'user'@'192.168.1.%'
    • 使用强密码
    • 定期审计用户权限

验证远程连接时,可以使用以下命令测试:

mysql -h <服务器IP> -u root -p