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

时间:2017-01-10 07:38:20

标签: mysql sql left-join database-administration

如果有三个表,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行具有最大匹配列。

我上面三个表的连接输出应该是,

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)

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

请建议。

4 个答案:

答案 0 :(得分:1)

这里的问题是: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

答案 1 :(得分:0)

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)
);

答案 2 :(得分:0)

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

关键是计算排名,以便确定具有最佳匹配的TablePqrs行。另一方面,如果两行对同一项值具有相同的排名,我们必须使用其他条件来撤消平局。在示例中,条件是TableAbcd表的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 

答案 3 :(得分:0)

我尝试了下面的事情并且它起作用了,合并帮助我根据我提到的顺序优先选择哪个值。

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