如何找到具有最大匹配列的行?

How to find the row with the maximum matching columns?

如果有三个table,TableItem、TableAbcd和TablePqrs,如下

TableItem
ID  item
1   item1

TableAbcd
ID  Item    ColA    ColB    ColC    ColD
1   item1   A1      B1      C1      D1


TablePqrs
ID  item    ColA    ColB    ColC    ColD    ColValue
1   item1   A1      B1      null    null    10000
2   item1   A1      B1      C1      D1      100

在这里,对于给定的项目,输出中必须只有一条记录,其最大列数与 TableAbcd 和 TablePqrs 匹配。 由于 TableAbcd 的第 1 行具有与 TablePqrs 第 2 行的最大匹配列。

我加入以上三个 table 的输出应该是,

item    ColA    ColB    ColC    ColD    ColValue
item1   A1      B1      C1      D1      100

到目前为止已经尝试过代码,

Select  item,   ColA,   ColB,   ColC,   ColD,   ColValue
     FROM TableItem a 
         LEFT OUTER JOIN TableAbcd b
         ON a.item = b.item      
         LEFT OUTER JOIN TablePqrs c
         ON (b.ColA = c.ColA AND b.ColB = c.ColB AND b.ColC = c.ColC AND b.ColD = c.ColD)
         OR (b.ColA = c.ColA AND b.ColB = c.ColB AND b.ColC = c.ColC)
         OR (b.ColA = c.ColA AND b.ColB = c.ColB)

如果获取的是我的两条记录,我知道可能存在设计问题,但我们正在从第三方遗留系统获取数据,该系统根据需要具有 table 结构并将其发送到另一个接口。

求推荐。

这里的问题是:B和C匹配了多少列?

对于 join 子句,您只需要 b 的至少一列与 c 中的同一列相匹配:

from c
left join b
     on c.A = b.A or c.B = b.B or c.C = b.C or c.D = b.D

您可以通过以下方式计算:

(case when c.A = b.A then 1 else 0 end) 
+ (case when c.B = b.B then 1 else 0 end)
+ (case when c.C = b.B then 1 else 0 end)
+ (case when c.D = b.D then 1 else 0 end) as matches

然后简单地通过匹配行(后代)排序并将结果限制为 1 行。

select 
   c.id, c.item, c.A, c.B, c.C, c.D, c.colValue,
   (case when c.A = b.A then 1 else 0 end) 
   + (case when c.B = b.B then 1 else 0 end)
   + (case when c.C = b.B then 1 else 0 end)
   + (case when c.D = b.D then 1 else 0 end) as matches
from c
left join b
     on c.A = b.A or c.B = b.B or c.C = b.C or c.D = b.D
order by
   ((case when c.A = b.A then 1 else 0 end) 
   + (case when c.B = b.B then 1 else 0 end)
   + (case when c.C = b.B then 1 else 0 end)
   + (case when c.D = b.D then 1 else 0 end)) desc
limit 1;

我设置了一个 rextester 示例只是为了检查它:http://rextester.com/IPA67860

TableAbcd调用aTablePqrs调用p,匹配数为(p.cola = a.cola) + (p.colb = a.colb) + (p.colc = a.colc) + (p.cold = a.cold),因为在MySQL中true 是 1,false 是 0。

现在您正在查找 p 条记录,其中不存在其他 p 条记录且匹配次数较多:

select *
from tablepqrs p1
where not exists
(
  select *
  from tablepqrs p2
  join tableabcd a on a.item = p2.item
  where p2.item = p1.item
  and (p2.cola = a.cola) + (p2.colb = a.colb) + (p2.colc = a.colc) + (p2.cold = a.cold) >
      (p1.cola = a.cola) + (p1.colb = a.colb) + (p1.colc = a.colc) + (p1.cold = a.cold)
);

在下面的代码中,您可以看到另一个过滤选项。它与 McNets 提出的方法类似,但使用 window 函数。

关键是计算一个排名,该排名允许确定具有最佳匹配的 TablePqrs 行。另一方面,如果两行对于相同的项目值具有相同的排名,我们必须使用额外的标准来取消平局。在示例中,条件是 TableAbcd table 的 ID。我没有使用外部联接,因此没有匹配排名的 TableItems 记录将没有结果。

我不太确定它是否真的符合您真正想要的,请尝试一下并得出自己的结论。

SELECT TableItem.id, 
       TableItem.item, 
       TablePqrs.colA, 
       TablePqrs.colB, 
       TablePqrs.colC, 
       TablePqrs.colD, 
       TablePqrs.value
  FROM TableItem
  INNER JOIN (SELECT DISTINCT 
                     tableItemId, 
                     FIRST_VALUE(tablePqrsId) OVER (PARTITION BY tableItemId ORDER BY ranking DESC, tablePqrsId DESC) tablePqrsId  
          FROM (SELECT rankTableItem.ID tableItemId, 
                       rankTablePqrs.ID tablePqrsId, 
                       CASE WHEN rankTablePqrs.colA IS NULL THEN 0 ELSE 1 END + 
                       CASE WHEN rankTablePqrs.colB IS NULL THEN 0 ELSE 1 END +
                       CASE WHEN rankTablePqrs.colC IS NULL THEN 0 ELSE 1 END +
                       CASE WHEN rankTablePqrs.colD IS NULL THEN 0 ELSE 1 END ranking
                  FROM TableItem rankTableItem 
                  INNER JOIN TableAbcd rankTableAbcd ON rankTableItem.item = rankTableAbcd.item      
                  INNER JOIN TablePqrs rankTablePqrs ON rankTablePqrs.item = rankTableAbcd.item 
                                                        AND (rankTableAbcd.colA = rankTablePqrs.colA 
                                                              OR rankTableAbcd.colB = rankTablePqrs.colB 
                                                              OR rankTableAbcd.colC = rankTablePqrs.colC 
                                                              OR rankTableAbcd.colD = rankTablePqrs.colD))) pivotTable ON pivotTable.tableItemId = TableItem.Id
  INNER JOIN TablePqrs ON TablePqrs.Id = pivotTable.tablePqrsId 

我尝试了下面的方法并且成功了,合并帮助我根据我在其中提到的顺序优先选择哪个值。

Select  item,   ColA,   ColB,   ColC,   ColD,   ColValue
     FROM TableItem a 
    LEFT OUTER JOIN (
         SELECT item,
            COALESCE(c1.ColValue,c2.ColValue,c3.ColValue) ColValue
        FROM abc b
        LEFT OUTER JOIN pqr c1 
            ON b.ColA = c1.ColA AND b.ColB = c1.ColB AND b.ColC = c1.ColC AND b.ColD = c1.ColD
        LEFT OUTER JOIN pqr c2 
            ON b.ColA = c2.ColA AND b.ColB = c2.ColB AND b.ColC = c2.ColC
        LEFT OUTER JOIN pqr c3 
            ON b.ColA = c3.ColA AND b.ColB = c3.ColB
        GROUP BY item
     ) as Fact 
     ON Fact.item = a.item