我有一个关于(至少对我来说)复杂的SQL语句的问题。给定interface
我想找到所有access_lists
,可以在interface
上使用。我正在创建一个Ruby on Rails应用程序,该任务的工作Ruby代码如下(应该是非常明显的):
class Interface < ActiveRecord::Base
def valid_access_lists
# find all valid networks (.ip() converts object into manageable IP object)
valid_networks = self.routes.map { |route| route.network.ip() }
return AccessList.includes(access_list_elements: [{ rule: [:ip_from] }]).all().select do |acl|
# all rule.ip_froms are in valid networks?
acl.access_list_elements.all? do |acle|
# current rule.ip_from is in any of the valid networks?
valid_networks.any? { |network| network.include?(acle.rule.ip_from.ip()) }
end
end
end
end
问题在于,它(正如预期的那样)超级慢(是的,includes
阻止了N + 1个查询)。
我解决此问题的方法是编写一个SQL语句,该语句执行valid_access_lists
一次执行的所有操作。
以下语句将valid_networks
作为((network_addr1, broadcast_addr1), (network_addr2, broadcast_addr2), ...)
。这里有一个问题:它只考虑IPv4地址(当使用逻辑OR计算广播地址时可以在第2行看到)。如果ips
中的记录是IPv4,ips.ip_high IS NULL
,则两列都是NOT NULL
。通过执行(ip_high << 64) + ip_low
构建IPv6地址。
计算网络和广播地址的想法是使用ips
运算符检查rules
的{{1}}。
BETWEEN
此语句连接表以启用基于SELECT coalesce(nw_ips.`ip_high` << 64, 0) + nw_ips.`ip_low` AS nw_addr,
(coalesce(nw_ips.`ip_high` << 64, 0) + nw_ips.`ip_low`) | ((1 << (32 - nw_ips.`prefix`)) - 1) AS bc_addr FROM `ips` nw_ips
LEFT JOIN `routes` ON nw_ips.`id` = `routes`.`ip_network_id`
LEFT JOIN `interfaces` ON `routes`.`interface_id` = `interfaces`.`id`
WHERE `interfaces`.`id` = 1
rules
access_lists
这里的主要问题是,我需要按SELECT `access_lists`.* FROM `access_lists`
LEFT JOIN `access_list_elements` ON `access_lists`.`id` = `access_list_elements`.`access_list_id`
LEFT JOIN `rules` ON `access_list_elements`.`rule_id` = `rules`.`id`
LEFT JOIN `ips` ON `rules`.`ip_from_id` = `ips`.`id`
WHERE ...
对rules
(及其各自的ips
)进行分组,以便检查access_list
中是否rules
{1}}至少包含在access_list
以及我被卡住的地方。