如何找到具有最大匹配列的行?
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
调用a
,TablePqrs
调用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
如果有三个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
调用a
,TablePqrs
调用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