crossjoin()是唯一能生成标准sql cross join的方法;join()不传on条件会抛出invalidargumentexception,因底层强制要求关联条件,而交叉连接本质是无条件组合。

在 Laravel 中实现用户、技能经验与连续日期的三表交叉连接查询,必须用 crossJoin() 显式声明笛卡尔积逻辑,否则关联字段无法对齐、日期子查询报错或退化为内连接。
为什么不能用 join() 模拟交叉连接
crossJoin() 是唯一能生成标准 SQL CROSS JOIN 的方法;【join() 即使不传 on 条件,Laravel 7+ 也会抛出 InvalidArgumentException: “You must specify a join clause.”】。底层强制要求关联条件,而交叉连接的本质就是无条件组合——强行用 join('date_gen') 会直接中断执行,根本走不到后续 leftJoin() 步骤。
你若尝试 DB::table('users')->join('date_gen') → 立刻报错,连 SQL 都没发出去。
构建带变量初始化的日期子查询
MySQL 的 @i 变量在 Laravel 查询构造器中不会自动初始化,必须在子查询内部显式声明 (select @i:=0)。漏掉这句,date_add 表达式里的 @i 始终为 NULL,最终生成全 NULL 的 date_gen 列。
方法一:用 DB::raw() 包裹完整子查询并加别名
DB::raw('(select date_add(curdate() - interval 10 day, interval @i:=@i+1 - 1 day) as date_gen from information_schema.columns, (select @i:=0) gen_sub limit 11) as date_gen')
【别名 as date_gen 必须存在,否则 crossJoin() 无法识别该子查询为合法表源】
方法二:若需复用该日期逻辑,可封装为视图或临时表,但跨环境部署时不如 DB::raw() 直接可控。
执行交叉连接并左连技能经验表
第一步:调用 crossJoin() 传入上一步的 DB::raw() 子查询
第二步:leftJoin('skill_exp', function ($join) { $join->on('users.id', '=', 'skill_exp.user_id')->on('date_gen.date_gen', '=', 'skill_exp.date'); })
第三步:select() 中所有字段必须加表前缀,否则字段冲突或值被覆盖
例如:select('users.username', 'date_gen.date_gen as date', 'skill_exp.level') ✅;写成 'date_gen as date' ❌(缺前缀导致 MySQL 无法解析)
第四步:添加 groupBy 防止严格模式报错
→groupBy('users.id', 'date_gen.date_gen')











