mysql常用json查询

  • 格式化数据格式

-- 格式化DECIMAL
select
    CONVERT(
        total_money,
        DECIMAL(15, 2)
    ) as convert_money
from
    `agent_data`

convert_money|
-------------+
      8840.00|
         0.00|

-- 格式化时间
select
    CONVERT(created_at, DATE) as created_at,
    CONVERT(created_at, DATETIME) as created_at2
from
    `agent_data`

created_at|created_at2        |
----------+-------------------+
2022-05-27|2022-05-27 15:22:59|
2022-05-27|2022-05-27 15:31:10|
  • 保留小数位数,多的截取

select
    TRUNCATE(total_money, 2) as truncate_money
from
    `agent_data`

truncate_money|
--------------+
          8840|
  • IFNULL 判断

-- 在SUM等计算的时候,如果任何参数为null,那么计算的结果也将会是null,是不对的,需要手动判断

select
    SUM(IFNULL(total_money, 0)) as sum_total_money
from
    `agent_data`

sum_total_money|
---------------+
          10491|
  • IF 判断

-- 如果type==1或者==2统计total_money字段否则统计money字段
select
    SUM(
        IF(
            `type` = 1
            OR `type` = 2,
            `total_money`,
            `money`
        )
    ) as `sum_total_money`
from
    `agent_data`

sum_total_money|
---------------+
           9715|
  • 逗号分割的文本数据进行查询

-- FIND_IN_SET(needle,haystack);
-- 第一个参数needle是要查找的字符串。第二个参数haystack是要搜索的逗号分隔的字符串列表。
select
    `remark_name`,
    FIND_IN_SET("员工备注2", `remark_name`) as find_num
from
    `agent_data`
WHERE
    FIND_IN_SET("员工备注2", `remark_name`) > 0

remark_name|find_num|
-----------+--------+
员工备注2      |       1|
  • json格式读取

-- 索引json数据值查询

select
    rule_key,
    JSON_EXTRACT(rule_key, '$[0]') as json_data_1
from
    `agent_data`
WHERE
    JSON_EXTRACT(rule_key, '$[0]') = 'force_popup'

rule_key                                                                                                                                                                                                                                                       |json_data_1  |
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+
["force_popup"]                                                                                                                                                                                                                                                |"force_popup"|
["force_popup"]                                                                                                                                                                                                                                                |"force_popup"|
["force_popup", "fixed_profit", "operating_amortization", "view_orders"]                                                                                                                                                                                       |"force_popup"|
["force_popup", "view_order", "view_order_mobile_number_display"]                                                                                                                                                                                              |"force_popup"|

-- 键值对json数据值查询

select
    `compute_arr`,
    json_extract(`compute_arr`, '$."first"') as json_first,
    json_extract(`compute_arr`, '$."first"."3"') as json_first_3
from
    `order`
WHERE
    json_extract(`compute_arr`, '$."first"') IS NOT NULL

compute_arr                                                                                          |json_first                                          |json_first_3|
-----------------------------------------------------------------------------------------------------+----------------------------------------------------+------------+
{"first": {"3": -480, "80": 70, "81": 30, "92": 165, "93": 88}, "second": {"3": 1, "80": 0, "81": 0}}|{"3": -480, "80": 70, "81": 30, "92": 165, "93": 88}|-480        |
{"first": {"3": -480, "80": 70, "81": 30, "92": 165, "93": 88}, "second": {"3": 1, "80": 0, "81": 0}}|{"3": -480, "80": 70, "81": 30, "92": 165, "93": 88}|-480        |

-- 索引包含键值对json数据值查询

select
    `data`,
    json_extract(`data`, '$[*]."field"') as `field_arr`
from
    `agent_log`
WHERE
    id = 106

data                                                                                                                                                                                                              |field_arr                                                               |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------+
[{"field": "equipment_amortization_all_money", "title": "金额", "value": "2.00", "oldvalue": "1.00"}, {"field": "equipment_amortization_day_money", "title": "设备每天的金额", "value": "3.00", "oldvalue": "2.00"}]|["equipment_amortization_all_money", "equipment_amortization_day_money"]|
  • json匹配

-- JSON_CONTAINS() 函数
-- target_json必需的。一个 JSON 文档。candidate_json 必需的。被包含的 JSON 文档。 path 可选的。一个路径表达式。

-- 匹配json,索引类型: 直接匹配对应的value即可

select
    rule_key,
    JSON_CONTAINS(
        rule_key,
        '["fixed_profit", "operating_amortization"]'
    ) as check_num,
    JSON_CONTAINS(
        rule_key,
        '["fixed_profit"]'
    ) as check_num2
from
    `agent_data`
WHERE
    id = 39

rule_key                                                                |check_num|check_num2|
------------------------------------------------------------------------+---------+----------+
["force_popup", "fixed_profit", "operating_amortization", "view_orders"]|        1|         1|

-- 匹配json,键值对类型: 对 compute_arr.first.3 的值进行匹配, 注意-480的数值类型需要一致

select
    `compute_arr`,
    `compute_arr` -> '$."first"."3"',
    JSON_CONTAINS(
        `compute_arr` -> '$."first"."3"',
        '-480'
    ) as check_num
from
    `order`

compute_arr                                                                                          |`compute_arr` -> '$."first"."3"'|check_num|
-----------------------------------------------------------------------------------------------------+--------------------------------+---------+
{"first": {"3": -480, "80": 70, "81": 30, "92": 165, "93": 88}, "second": {"3": 1, "80": 0, "81": 0}}|-480                            |        1|
{"first": {"3": -480, "80": 70, "81": 30, "92": 165, "93": 88}, "second": {"3": 1, "80": 0, "81": 0}}|-480                            |        1|


-- 匹配json,索引包含键值对类型: 索引包含键值对json数据值查询
select
    `data`,
    JSON_CONTAINS(
        `data` -> "$[*].field",
        '["equipment_amortization_all_money"]'
    ) as check_num
from
    `agent_log`
WHERE
    id = 106

data                                                                                                                                                                                                              |check_num|
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------+
[{"field": "equipment_amortization_all_money", "title": "设备金额", "value": "2.00", "oldvalue": "1.00"}, {"field": "equipment_amortization_day_money", "title": "设备每天金额", "value": "3.00", "oldvalue": "2.00"}]|        1|
  • 分组查询之后获取对应分组的字段的所有值

select `key`, `type` from `rule`;

key                             |type             |
--------------------------------+-----------------+
force_popup                     |agent            |
no_withdrawal_privileges        |agent            |
order_details                   |agent            |
lending                         |agent            |
no_withdrawal_privileges        |employee         |
force_popup                     |employee         |
lend_charging_privileges        |employee         |
view_order                      |employee         |

select `key`, `type`,GROUP_CONCAT(`key`) from `rule` GROUP BY `type`;

key                     |type             |type_all_key
------------------------+-----------------+----------------------------------------------
force_popup             |agent            |force_popup,no_withdrawal_privileges,order_det
no_withdrawal_privileges|channeler        |no_withdrawal_privileges,order_info
order_info              |customer_servicer|order_info
no_withdrawal_privileges|employee         |no_withdrawal_privileges,force_popup,lend_char
order_info              |logisticser      |order_info
no_withdrawal_privileges|outlets          |no_withdrawal_privileges,no_login,order_info
  • json 数组操作新增和删除

/*
原数据:

["tixian_weixin", "dingdan_mx", "tixian_zfb"]

新增: 查询第一个匹配的字符串, 为null就新增
*/
UPDATE
    outlets
SET
    rule_key = IF(JSON_SEARCH(IFNULL(rule_key, '[]'),
    'one',
    'tixian_zfb') IS NULL,
    JSON_MERGE_PRESERVE(IFNULL(rule_key, '[]'),
    '["tixian_zfb"]'),
    rule_key)
where
    id = 1


/* 删除 */
UPDATE
    outlets
SET
    rule_key = IF(JSON_SEARCH(IFNULL(rule_key, '[]'),
    'one',
    'tixian_zfb') IS NULL,
    rule_key,
    JSON_REMOVE(rule_key,
    JSON_UNQUOTE(JSON_SEARCH(IFNULL(rule_key, '[]'),
    'one',
    'tixian_zfb'))))
where
    id = 1
-- 前7天的数据
created_at >= CURDATE() - INTERVAL 7 DAY

-- 计算当前时间和receiving_tim+48小时的差值(秒)
TIMESTAMPDIFF(SECOND, NOW(), receiving_time + interval 48 hour)

-- 60 后参-前参数>0就是正数
SELECT TIMESTAMPDIFF(SECOND, '2023-01-01 12:00:00', '2023-01-01 12:01:00');
  • mysql一张表同时select和update,报错:You can‘t specify target table ‘xxx‘ for update in FROM clause

-- 把in的数据进行表缓存, 从缓存表里面拿数据id
-- ! 更新数据作为条件时, 需要使用缓存数据: 就是在条件里面把数据查出来
-- You can't specify target table 'work_order' for update in FROM clause
update
  store_process
set
  local_store_id = 5
where
  id in (
    SELECT temp.id FROM
    (
      select
      store_process.id
    from
      store_process
      left join order_third_info on order_third_info.biz_order_id = store_process.order_third_no
    where
      order_third_info.identify_store_id > 0
      and order_third_info.identify_store_id = 10502
      and order_third_info.biz_order_id = '4131888375398986742'
    ) temp
  )
  • 常见操作

-- 备份表数据(主键一致)
CREATE TABLE migrations_bak AS
SELECT
	*
FROM
	migrations
WHERE
	batch = 2

-- 把一个表数据插入到另一个表(主键一致)
INSERT
	INTO
	migrations
SELECT
	*
FROM
	migrations_bak
WHERE
	batch = 2
-- 只插入非主键数据,指定字段(字段对应, 主键自增)
INSERT
	INTO
	migrations (migration,
	batch)
SELECT
	`migration`,
	`batch`
FROM
	migrations_bak
WHERE
	batch = 2

-- 关联表更新(使用INNER,只有两边都有数据才更新)
UPDATE
	migrations
INNER JOIN migrations_bak ON
	migrations.id = migrations_bak.id
SET
	migrations.batch2 = migrations_bak.batch
where
	migrations.id > 0
  • 最简单的去重,不用distinct而用min/max

select
	 *,min(order_third_no)
from
	wine_data.store_process
where
	order_third_no = 'MN63772110651040152'
    group by order_third_no
  • 查询不在某些数据的数据

-- 查询1,2,99993000不在wine_identify_code.id的数据
SELECT A.*, B.id FROM (
  SELECT '1' AS in_id UNION ALL
  SELECT '2' UNION ALL
  SELECT '99993000'
) AS A left join wine_identify_code B on A.in_id = B.id
where B.id is NULL


in_id   |id|
--------+--+
99993000|  |
  • 统计json字段的数据的和

select
	SUM(JSON_LENGTH(img_list)) as total_items
from
	store_device_img
  • 使用GROUP_CONCAT之前设置最大字节显示, 默认是1024字节完全不够用

Db::select("SET SESSION group_concat_max_len = 1024 * 100");
// 再使用
Db::select("select
        MIN(id), GROUP_CONCAT(id)
    from
        store_process");
$count_arr2 = WineIdentify::selectRaw("
        DATE(created_at) AS date,
        COUNT(CASE WHEN appraisal_results = 1 THEN 1 ELSE NULL END) AS approved_count,
        COUNT(CASE WHEN appraisal_results = 2 THEN 1 ELSE NULL END) AS rejected_count,
        COUNT(CASE WHEN appraisal_results = 0 THEN 1 ELSE NULL END) AS pending_count,
        SUM(CASE WHEN (balance_money > 0) THEN balance_money ELSE 0 END) AS '预存金额'
        ")
->whereRaw("created_at >= CURDATE() - INTERVAL $day_number DAY")
->groupBy(Db::raw('DATE(created_at)'))
->orderBy('created_at')
->get()->toArray();
  • 使用Eloquent ORM in Laravel技巧

// ! 派生表查询(sql子表)
# toRawSql()获取已经原生sql

$sub = DB::table('merchant')
    ->selectRaw("
        id,
        DATE_FORMAT(created_at, '%Y-%m-%d') as date_new
    ")
    ->groupBy('date_new');
DB::table(DB::raw("({$sub->toSql()}) as _sub"))
    ->where('date_new', '=', "2025-07-29")
    ->select()
    ->get();

// ->leftJoin(DB::raw("({$use_money_for_uid->toRawSql()}) as _sub"), '_sub.merchant_id', '=', 'merchant.id');
// 实际生成sql
select
	*
from
	(
	select
		id,
		DATE_FORMAT(created_at, '%Y-%m-%d') as date_new
	from
		`merchant`
	group by
		`date_new`
	) as _sub
where
	`date_new` = '2025-07-18'


// 实际sql
select
		merchant.id
from
		`merchant`
left join `product_account` as `product_account` on
		`merchant`.`id` = `product_account`.`merchant_id`
left join `product_account_consume` as `product_account_consume` on
		`product_account`.`id` = `product_account_consume`.`account_id`
left join (
	select
			merchant_id,
			SUM(use_money) as use_money
	from
			`product_account`
	where
			`merchant_id` > 0
	group by
			`merchant_id`) as _sub on
		`_sub`.`merchant_id` = `merchant`.`id`
where
		`product_account_consume`.`date` between '2025-08-12 00:00:00' and '2025-08-12 23:59:59'
	and `merchant`.`id` > '0'
group by
		`merchant`.`id`
// ! 典型示例: 查询累计明细超过100和100以下的用户的数量
$sub = DB::table('product_account_consume')
    ->selectRaw("
        account_id,
        SUM(spend) AS sum_spend
    ")
    ->groupBy('account_id');
DB::table(DB::raw("({$sub->toSql()}) as _sub"))
    ->selectRaw("
            CASE
                WHEN (sum_spend < 100) THEN '金额100以下'
                WHEN (sum_spend >= 100) THEN '金额100以上'
                ELSE '其他'
            END AS layer,
            count(account_id) AS count_num
    ")
    ->groupBy('layer')
    ->get();
  • 对查询的值别名

select
  supplier.email as "邮箱",
  CASE
    WHEN (supplier_money_log.change_type = "yusou") THEN '预收'
    WHEN (supplier_money_log.change_type = "order_lease") THEN '租赁订单消费'
    ELSE '其他'
  END AS "类型",
  supplier_money_log.remark as "备注"
  • 多个表全连接

select
	id
from
	merchant
union all
select
	id
from
	supplier
  • 时间字段比较, 当字段的值为null时会无法比较, 结果都是false

SELECT
   `order`.over_at,
   `order`.no
FROM
    `order`
WHERE
    `order`.over_at is null

over_at|no                       |
-------+-------------------------+
       |ZL-DD17555670189224000595|
       |ZL-DD17555681927177000349|
       |ZL-DD17555685535208000671|
       |CZ-DD17555687178142      |
       |CZ-DD17555689176141      |
       |CZ-DD17555836631024      |

-- 增加时间比较, 数据就会查不出来
SELECT
   `order`.over_at,
   `order`.no
FROM
    `order`
WHERE
    `order`.over_at is null
    and NOW() > over_at
  • 计算日期时间的时间戳秒间隔

select
	`order`.pay_at,
	`order`.created_at,
	UNIX_TIMESTAMP(pay_at) - UNIX_TIMESTAMP(created_at) as diff_seconds
from
	`order`
where
	`order`.pay_status = 20

pay_at             |created_at         |diff_seconds|
-------------------+-------------------+------------+
2025-07-25 11:22:18|2025-07-25 11:22:12|           6|
  • 如果要分组对某个字段的最大/最小的数据值需要额外进行关联表(关联表取分组后的极限值)

-- 取工单最新的一条操作日志
select
	work_order_id,
	id,
	created_at,
	type
from
	work_order_log
where
	id in (
		select
			max(id)
		from
			work_order_log
		group by
			work_order_id
	)
group by
	work_order_id
  • 使用已知的数据作为缓存表, 比如日期

$base = convertToUnionAllSql([
    ['date' => "2025-09-17"],
    ['date' => "2025-09-16"],
]);
Order::leftJoin(DB::raw("($base) as _temp"), function ($join) {
    $join->on('_temp.date', '=', Db::raw("DATE_FORMAT(order.created_at, '%Y-%m-%d')"));
})->count();
Db::table(DB::raw("($base) as _temp"))->leftJoin('order', function ($join) {
    $join->on('_temp.date', '=', Db::raw("DATE_FORMAT(order.created_at, '%Y-%m-%d')"));
})->count();

select
	count(*) as aggregate
from
	`order`
left join (
	select
		'2025-09-17' as date
union all
	select
		'2025-09-16') as _temp on
	`_temp`.`date` = DATE_FORMAT(order.created_at, '%Y-%m-%d')
//
select
	count(*) as aggregate
from
	(
	select
		'2025-09-17' as date
union all
	select
		'2025-09-16') as _temp
left join `order` on
	`_temp`.`date` = DATE_FORMAT(order.created_at, '%Y-%m-%d')

/**
 * 把列表的对象转为临时表
 *
 * @param array $data
[
    ['d' => 1],
    ['d' => 2],
]
 * @return string
 * @author jcleng
 */
function convertToUnionAllSql($data) {
    $lines = [];

    foreach ($data as $index => $row) {
        $values = [];
        foreach ($row as $key => $value) {
            // 转义单引号并包裹值
            $escaped = "'" . str_replace("'", "''", $value) . "'";

            // 第一行添加列别名
            if ($index === 0) {
                $values[] = $escaped . " AS " . $key;
            } else {
                $values[] = $escaped;
            }
        }

        $lines[] = "SELECT " . implode(", ", $values);
    }

    return implode("\nUNION ALL\n", $lines);
}
  • 多个字段排序

select
	*
from
	`order`
order by
	case
		when order_status = 150 then 0
		when order_status = 130 then 1
		else 3
	end asc,
	created_at asc;

-- 根据值排序
order by FIELD(user_lv, 'S', 'A', 'B', 'C', 'D');