PowerBI/DAX 计算列,用于从特定机器/网络 IP 和掩码计算网络 CIDR

问题描述

我的公司服务器上只安装了“基本”PBI,因此我需要使用 DAX 或 PowerQuery 找到解决方案。没有 Python,没有 R ... :/

我有两个包含网络 ip 和掩码的表,另一个包含机器 ip 和掩码。

我想设置两个表之间的关系。

我的想法是在每个表中计算一个新列。 它将是 CIDR 表示法中的网络 ip 和掩码, 我将使用它作为建立关系的常用键:

机器 --N:1--> 网络

Networks Table
==
netip,netmask          -> netcidr *
10.10.1.32,255.255.255.224  -> 10.10.1.32/27


Machines Table
==
machineip,netmask          -> netcidr *
10.10.1.35,255.255.255.224  -> 10.10.1.32/27
10.10.1.38,255.255.255.224  -> 10.10.1.32/27
  • 在这两种情况下,我都需要计算 netcidr 列。

我已经用 Python 函数完成了它,但我需要在 DAX、PowerQuery 或 sql 中进行转换:

def network2(ipmask):
    ip,cidr = ipmask.split('/')
    print(">",ip,cidr)
    a,b,c,d = ip.split('.')
    ipn = (((int(a)*256+int(b))*256)+int(c))*256+int(d)

    cidr = int(cidr)
    mask = (0xffffffff >> (32 - cidr)) << (32 - cidr)
    net = ipn & mask

    print (ipn,cidr,mask,net )
    
    a = net%(256**4)//(256**3)
    b = net%(256**3)//(256**2)
    c = net%(256**2)//(256**1)
    d = net%(256**1)//(256**0)
    
    #net IP decimal
    print(">>>old  - ip: ",ipmask)
    print(">>>nets - ip: ",a,d,"/",cidr)


    #ip,mask
    # for i in ip.split('.')

network2('10.10.1.32/27') #mask 255.255.255.224
network2('10.10.1.38/27')


>>>old  - ip:  10.10.1.32/27
>>>nets - ip:  10 10 1 32 / 27
>>>old  - ip:  10.10.1.38/27
>>>nets - ip:  10 10 1 32 / 27

它在 Python 中工作,现在我尝试在 DAX 中完成:

cidr_net = 
// ...
VAR mask = data[mask_lan]
VAR dot1 = FIND(".",1,0)
VAR p1   = VALUE(IF(dot1>0,(MID(mask,dot1-1)),"0"))
VAR dot2 = FIND(".",dot1+1,0)
VAR p2   = VALUE(IF(dot2>0,MID(mask,dot2-1-dot1),"0"))
VAR dot3 = FIND(".",dot2+1,0)
VAR p3   = VALUE(IF(dot3>0,dot3-1-dot2),"0"))
VAR p4   = VALUE(IF(dot3>0,dot3+1,len(mask)-dot3),"0"))

VAR ip    = data[ip_lan]
VAR d1    = FIND(".",0)
VAR ip1   = VALUE(IF(d1>0,(MID(ip,d1-1)),"0"))
VAR d2    = FIND(".",d1+1,0)
VAR ip2   = VALUE(IF(d2>0,MID(ip,d2-1-d1),"0"))
VAR d3    = FIND(".",d2+1,0)
VAR ip3   = VALUE(IF(d3>0,d3-1-d2),"0"))
VAR ip4   = VALUE(IF(d3>0,d3+1,len(ip)-d3),"0"))

VAR n1    = p1 && ip1
VAR n2    = p2 && ip2
VAR n3    = p3 && ip3
VAR n4    = p4 && ip4


VAR cidr = FORMAT( 32-log( 4294967296-((((p1*256+p2)*256)+p3)*256+p4),2),"##")
RETURN CONCATENATE(CONCATENATE(CONCATENATE(CONCATENATE(CONCATENATE(CONCATENATE(CONCATENATE(CONCATENATE(n1,"."),n2),n3),n4),"/"),cidr)

但它不起作用:

1/ 我在 DAX 中找不到按位运算符......所以“VAR n1 = p1 && ip1”不能作为按位和。是否可以按位和另一种方式进行?

2/ 在 DAX 中有没有其他方法可以更轻松地做到这一点?

3/ 否则可以使用 PowerQuery 吗?

解决方法

可以实施 DAX 解决方案,即使将此计算移至 SQL 或 Power Query 会更好

我想我修正了你代码的最后一步

docker run -p 8080:8080 -p 50000:50000 -d -v jenkins_home:/var/jenkins_home jenkins/jenkins:lts

this is a link to a working version of this code on DAX.do

,

我实现了一个 T-SQL 版本的转换。它使用 STRING_SPLIT() 函数,该函数自 SQL Server 2016 起可用。它非常复杂,而且从性能的角度来看也可能很糟糕。我不会在生产中使用它,但只是为了在这里了解它

首先我创建一个示例表

create table t( ip varchar(40) not null,mask varchar(40) not null);
insert into t(ip,mask) values ('10.0.1.38','255.255.255.224');

然后查询

WITH CTE AS (
SELECT ip,mask,B.ipn,M.masknc
from t AS A 
CROSS APPLY ( 
SELECT (([1] * 256 + [2]) * 256 + [3]) * 256 + [4] AS ipn FROM
(SELECT CAST([value] AS BIGINT) AS value,ID = ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) 
FROM STRING_SPLIT(A.ip,'.') ) AS src
PIVOT
(MAX(value) FOR ID in ([1],[2],[3],[4]) ) AS P ) AS B
CROSS APPLY ( 
SELECT CAST(256 AS BIGINT) * 256 * 256 * 256 - ((([1] * 256 + [2]) * 256 + [3]) * 256 + [4]) AS masknc FROM
(SELECT CAST([value] AS BIGINT) AS value,ID = ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) 
FROM STRING_SPLIT(A.mask,[4]) ) AS P ) AS M
),CTE1 AS (
SELECT C.ip,C.mask,CAST(C.ipn / C.masknc AS BIGINT) * C.masknc AS netipn,32 - LOG(C.masknc,2) AS maskc,E32 = CAST(256 AS BIGINT) * 256 * 256 * 256,E24 = CAST(256 AS BIGINT) * 256 * 256,E16 = CAST(256 AS BIGINT) * 256,E8 = CAST(256 AS BIGINT)
FROM CTE AS C
),CTE2 AS (
SELECT C.ip,netip1 = FLOOR(netipn / E24),netip2 = FLOOR((netipn % E24)/ E16),netip3 = FLOOR((netipn % E16) / E8),netip4 = FLOOR(netipn % E8),C.maskc
FROM CTE1 AS C
)
SELECT C.ip,CAST(C.netip1 AS VARCHAR(3)) + '.' + CAST(C.netip2 AS VARCHAR(3)) + '.' +
CAST(C.netip3 AS VARCHAR(3)) + '.' + CAST(C.netip4 AS VARCHAR(3)) + '/' + CAST(C.maskc AS VARCHAR(4)) AS netip  
FROM CTE2 AS C

这是此代码在 dbfiddle.uk 上的链接

我认为最好的选择可能是在 Power Query 中使用 M。