Mysql Rails基于真值表计算唯一记录的复杂查询

Mysql Rails基于真值表计算唯一记录的复杂查询,mysql,sql,ruby-on-rails,Mysql,Sql,Ruby On Rails,使用Rails。我有以下代码: class-TypeOfBlock1,“T”=>1,“UP”=>2} 提供的答案是正确的。我只想说: 如您所见,查询不会返回零计数,这必须是 在ruby代码中解决(可能使用所有组合初始化哈希) 然后与查询计数合并) 可以通过使用纯MySQL实现: SELECT sub.combination, COALESCE(cnt, 0) AS cnt FROM (SELECT GROUP_CONCAT(Name ORDER BY Name SEPARATOR ' + ')

使用Rails。我有以下代码:

class-TypeOfBlock
使用以下几组表格:

╔══════════════╗
║块的类型║
╠══════╦═══════╣
║ 身份证件║ 名称║
╠══════╬═══════╣
║  1.║ 向上的║
║  2.║ 陆上通信线║
║  3.║ T║
╚══════╩═══════╝
╔═══════════════════════════════╗
║    患者\u类型\u块║ 
╠══════════════════╦════════════╣
║ 块id的类型║ 病人编号║
╠══════════════════╬════════════╣
║                1.║          1.║
║                1.║          2.║
║                2.║          2.║
║                3.║          3.║
║                2.║          4.║
║                1.║          5.║
║                1.║          6.║
║                2.║          6.║
║                3.║          6.║
╚══════════════════╩════════════╝ 
我想根据区块组合的类型计算唯一患者的数量,以下是预期结果:

#预期结果(就像真值表)
UP(仅具有类型为1的患者)=2名患者
UP+LL(类型为1和2的患者)=1名患者
UP+T(类型为1和3的患者)=0名患者
LL(仅限类型为2的患者)=1名患者
LL+T(具有类型为2和3的患者)=0名患者
T(仅具有类型为3的患者)=1名患者
UP+LL+T(类型为1、2和3的患者)=1名患者
我尝试加入如下表:

up\ll=
块的类型。
联接(“联接患者\u块的类型\u块上的患者\u块的类型\u块。类型\u块的类型\u块的id=类型\u块的类型\u块的id”)。
其中(“患者\u类型\u块中的\u块。患者\u类型\u块中的\u块id=1,患者\u类型\u块中的\u块。患者\u类型\u块中的\u块id=2”)。
大小
但这太复杂了,数字是错误的。我想尝试原始SQL,但Rails 4不推荐使用它,并要求我执行
ModelClass.find\u by\u SQL


如何生成上述预期结果?

我想到的唯一解决方案是使用原始SQL并利用,如图所示

需要的SQL是:

选择
组合,
将(*)计数为cnt
从(
挑选
ptb.patient_id,
组_concat(tb.name按tb.name排序)作为组合
来自\u块tb的类型\u
内部连接患者\u类型\u块ptb上的ptb。类型\u块\u id=tb.id
按ptb分组。患者id)患者组合
组合分组;
内部选择由患者分组,并选择每个患者拥有的块类型组合。然后,外部选择只对每个组合中的患者进行计数

查询将返回以下内容(请参阅):

正如您所看到的,查询不会返回零计数,这必须在ruby代码中解决(可能会使用零的所有组合初始化哈希,然后与查询计数合并)

要将此查询集成到ruby,只需在任何模型上使用
find\u by\u sql
方法(例如,将结果转换为哈希):

sql=1,“LL,T,UP”=>1,“LL,UP”=>1,“T”=>1,“UP”=>2}
提供的答案是正确的。我只想说:

如您所见,查询不会返回零计数,这必须是 在ruby代码中解决(可能使用所有组合初始化哈希) 然后与查询计数合并)

可以通过使用纯MySQL实现:

SELECT sub.combination, COALESCE(cnt, 0) AS cnt
FROM (SELECT GROUP_CONCAT(Name ORDER BY Name SEPARATOR ' + ') AS combination
      FROM (SELECT p.Name, p.rn, LPAD(BIN(u.N + t.N * 10), size, '0') bitmap
            FROM (SELECT @rownum := @rownum + 1 rn, id, Name
                  FROM type_of_blocks, (SELECT @rownum := 0) r) p
            CROSS JOIN (SELECT 0 N UNION ALL SELECT 1 
                    UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
                    UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7
                    UNION ALL SELECT 8 UNION ALL SELECT 9) u
             CROSS JOIN (SELECT 0 N UNION ALL SELECT 1 
                    UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
                    UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7
                    UNION ALL SELECT 8 UNION ALL SELECT 9) t
             CROSS JOIN (SELECT COUNT(*) AS size FROM type_of_blocks) o
             WHERE u.N + t.N * 10 < POW(2, size)
             ) b
       WHERE SUBSTRING(bitmap, rn, 1) = '1'
       GROUP BY bitmap
) AS sub
LEFT JOIN (
    SELECT combination, COUNT(*) AS cnt
    FROM (SELECT ptb.patient_id,
                GROUP_CONCAT(tb.name ORDER BY tb.name SEPARATOR ' + ') AS combination
          FROM type_of_blocks tb
          JOIN patients_type_of_blocks ptb 
            ON ptb.type_of_block_id = tb.id
          GROUP BY ptb.patient_id) patient_combinations
    GROUP BY combination   
) AS sub2
  ON sub.combination = sub2.combination
ORDER BY LENGTH(sub.combination), sub.combination; 
工作原理:

  • 使用所述方法生成所有可能的组合(略有改进)
  • 计算可用的组合
  • 结合两种结果

  • 要更好地了解其工作原理,请参见生成所有社区的Postgresql版本:

    WITH all_combinations AS (
        SELECT string_agg(b.Name ,' + ' ORDER BY b.Name) AS combination
        FROM (SELECT p.Name, p.rn, RIGHT(o.n::bit(16)::text, size) AS bitmap
              FROM (SELECT *, ROW_NUMBER() OVER(ORDER BY id)::int AS  rn
                    FROM type_of_blocks )AS p
              CROSS JOIN generate_series(1, 100000) AS o(n)     
              ,LATERAL(SELECT COUNT(*)::int AS size FROM type_of_blocks) AS s
              WHERE o.n < 2 ^ size
             ) b
        WHERE SUBSTRING(b.bitmap, b.rn, 1) = '1'
        GROUP BY b.bitmap
    )
    SELECT sub.combination, COALESCE(sub2.cnt, 0) AS cnt
    FROM all_combinations sub
    LEFT JOIN (SELECT combination, COUNT(*) AS cnt
               FROM (SELECT ptb.patient_id,
                     string_agg(tb.name,' + ' ORDER BY tb.name) AS combination
                     FROM type_of_blocks tb
                     JOIN patients_type_of_blocks ptb 
                       ON ptb.type_of_block_id = tb.id
                     GROUP BY ptb.patient_id) patient_combinations
               GROUP BY combination) AS sub2
      ON sub.combination = sub2.combination
    ORDER BY LENGTH(sub.combination), sub.combination; 
    
    将所有_组合作为(
    选择字符串_agg(b.Name,“+”按b.Name排序)作为组合
    从(选择p.Name、p.rn、RIGHT(o.n::bit(16)::文本、大小)作为位图
    从(选择*,行号()到(按id排序)::int作为rn
    从类型_到类型_块)为p
    交叉连接生成_系列(100000)为o(n)
    ,侧向(选择计数(*)::int作为块类型的大小)作为s
    其中o.n<2^尺寸
    )b
    其中子字符串(b.bitmap,b.rn,1)='1'
    按b位图分组
    )
    选择sub.combination,合并(sub2.cnt,0)作为cnt
    从所有_组合子
    左连接(选择组合,计数(*)为cnt
    从(选择ptb.patient_id、,
    字符串_agg(tb.name,“+”按tb.name排序)作为组合
    来自\u块tb的类型\u
    连接患者\u类型\u块ptb
    在ptb.type_of_block_id=tb.id上
    按ptb分组。患者id)患者组合
    按组合分组)作为子2
    关于sub.composition=sub2.composition
    按长度排序(子组合),子组合;
    

    您也可以只使用rails

    期望

    class PatientsTypeOfBlock < ActiveRecord::Base
      belongs_to :patient
      belongs_to :type_of_block
    end
    

    这太棒了。仅仅将此标记为答案并不公平。我会再等一天,然后悬赏你。非常感谢。谢谢你,我的荣幸!:)很高兴你发现了打字错误(我在本地的不同型号上进行了测试)。非常感谢@lad2025。直到。不幸的是,我已经把赏金给了博拉玛。再次感谢。哇!我仍然找到红宝石
    ╔══════════════╦═════╗
    ║ combination  ║ cnt ║
    ╠══════════════╬═════╣
    ║ T            ║   1 ║
    ║ LL           ║   1 ║
    ║ UP           ║   2 ║
    ║ LL + T       ║   0 ║
    ║ T + UP       ║   0 ║
    ║ LL + UP      ║   1 ║
    ║ LL + T + UP  ║   1 ║
    ╚══════════════╩═════╝
    
    WITH all_combinations AS (
        SELECT string_agg(b.Name ,' + ' ORDER BY b.Name) AS combination
        FROM (SELECT p.Name, p.rn, RIGHT(o.n::bit(16)::text, size) AS bitmap
              FROM (SELECT *, ROW_NUMBER() OVER(ORDER BY id)::int AS  rn
                    FROM type_of_blocks )AS p
              CROSS JOIN generate_series(1, 100000) AS o(n)     
              ,LATERAL(SELECT COUNT(*)::int AS size FROM type_of_blocks) AS s
              WHERE o.n < 2 ^ size
             ) b
        WHERE SUBSTRING(b.bitmap, b.rn, 1) = '1'
        GROUP BY b.bitmap
    )
    SELECT sub.combination, COALESCE(sub2.cnt, 0) AS cnt
    FROM all_combinations sub
    LEFT JOIN (SELECT combination, COUNT(*) AS cnt
               FROM (SELECT ptb.patient_id,
                     string_agg(tb.name,' + ' ORDER BY tb.name) AS combination
                     FROM type_of_blocks tb
                     JOIN patients_type_of_blocks ptb 
                       ON ptb.type_of_block_id = tb.id
                     GROUP BY ptb.patient_id) patient_combinations
               GROUP BY combination) AS sub2
      ON sub.combination = sub2.combination
    ORDER BY LENGTH(sub.combination), sub.combination; 
    
    class PatientsTypeOfBlock < ActiveRecord::Base
      belongs_to :patient
      belongs_to :type_of_block
    end
    
    PatientsTypeOfBlock.joins( :type_of_block, :patient ).where( "type_of_blocks.name = ?", "UP" ).count