Power Query 系列 (18) - 参数化查询

参数化查询增加了查询的灵活性。Power Query 可以设置和管理参数,同一工作簿下所有查询都可以使用。


但查询参数需要进入 Power Query 界面中设置和维护却不太方便,所以从更实用的角度来说,可以将参数设置在 Excel 工作表中,然后将此参数加载到Power Query 作为筛选条件。本篇以使用 Access 查询设计器轻松构造复杂 SQL 语句 (2)相同的数据进行类似的处理。我们发现,PQ 因为有查询编辑器提供的可视化操作,处理过程 SQL 语句更加简单。为了方便理解,这里也贴出 Access 中的表结构和表关系图。示例数据也已经上传到 github


假设我们本次的处理要求是根据年份和月份为条件,得到所有物料的进出存报表。

获取数据连接

示例数据存放在 Access 数据库中,首先获取数据连接,将数据加载到 Power Query 中。操作步骤请参考Power Query 系列 (03) - 从数据库导入数据。因为不区分仓位,所以我们导入 3 个数据表的数据:

在这里插入图片描述
点击加载按钮,数据加载到 PQ。这个时候我们可能注意到一个现象,当有多于 1 个表数据加载的时候,默认就不会将数据上载到 Excel 工作表。如果我们需要将某个查询表的数据上载到工作表,选择右边部分的查询&连接,右键菜单中选择加载到菜单,选择数据上载的目的地。数据的目的地一共有 5 个,如下图所示。如果安装了 Power Pivot,可以将数据加载到数据模型

设置参数

在工作表中设置如下图所示的筛选条件,使用 Ctrl + T,将筛选条件变为表格,并命名为 Criteria。


将表格 Criteria 加载至 Power Query,查询中以此作为数据筛选的条件。

进出存查询

以 stock_movement_details 为基础进行加工,将 stock_movement_details 复制一个新的查询命名为 stock_balance。删除 stock_balance 查询中与数据处理不相关的字段。

我们注意到,stock_movement_details 自动维护了与 stock_movement_headers 查询的关系,这个是 PQ 的智能之处。如果从类似 csv 文件导入,则不会有查询表之间的关系,我们可以通过合并查询的方法来维护查询表之间的关系。

对 stock_movemnt_headers 字段进行展开操作,保留 MovementType 和 DocDate 两个字段


完成该步骤后查询编辑器的界面如下:


与 movement_types 查询表进行合并查询操作,获取 movement_types 查询表的 InOutSign 字段:

对 movement_types 字段进行展开操作,保留 InOutSign 字段。完成后查询编辑器的界面如下:

根据 DocDate 字段添加两列 TxYear 和 TxMonth。在 PQ 中添加日期列也是非常方便。选中 DocDate 这一列,然后切换到【添加列】选项卡:

得到下面的结果:

用同样的方法添加 TxMonth 列:


得到 TxMonth 列:


根据 InOutSign 列添加一个自定义列:

接下来添加计算列,根据交易的年月和 InOutSign 来确定数据应该放到哪一列,是期初数量,还是期间出入库数量等等。进入高级编辑器,在 M 语言步骤的最前面添加两个变量:

    yearCriteria = Criteria{0}[year],
    monthCriteria = Criteria{0}[month],


同样的方法添加 PeriodOut 列。

添加期末余额的计算列:


删除不相关的字段,删除后查询编辑器界面如下:

使用分组依据计算合计数:

完成后查询编辑器界面如下:


此时对应的 M 语言脚本如下:

let
    yearCriteria = Criteria{0}[year],
    monthCriteria = Criteria{0}[month],
    
    Source = Access.Database(
        File.Contents("C:\Users\StoneWM\Desktop\stocks.accdb"), [CreateNavigationProperties=true]),

    stockMovementDetails = Source{[Schema="",Item="stock_movement_details"]}[Data],

    DeletedUnrelated1 = Table.RemoveColumns(
        stockMovementDetails,{"DocNo", "StorageLocation", "Remarks"}),
        
    ExpandedHeaderCols = Table.ExpandRecordColumn(
        DeletedUnrelated1, 
        "stock_movement_headers", 
        {"MovementType", "DocDate"}, 
        {"MovementType", "DocDate"}),

    MergedWithMovementTypes = Table.NestedJoin(
        ExpandedHeaderCols, 
        {"MovementType"}, 
        movement_types, 
        {"MovementTypeID"}, 
        "movement_types", 
        JoinKind.LeftOuter),

    ExpandedMovementTypeCols = Table.ExpandTableColumn(
        MergedWithMovementTypes, 
        "movement_types", 
        {"InOutSign"}, 
        {"InOutSign"}),

    InsertedYearCol = Table.AddColumn(
        ExpandedMovementTypeCols, 
        "TxYear", 
        each Date.Year([DocDate]), 
        Int64.Type),

    InsertedMonthCol = Table.AddColumn(
        InsertedYearCol, 
        "TxMonth", 
        each Date.Month([DocDate]), 
        Int64.Type),

    InsertedActualQtyCol = Table.AddColumn(
        InsertedMonthCol, 
        "ActualQty", 
        each if [InOutSign]="+" then [Qty] else -1 * [Qty]),

    InsertedBeginQtyCol = Table.AddColumn(
        InsertedActualQtyCol, 
        "BeginQty", 
        each if [TxYear] < yearCriteria or ([TxYear]=yearCriteria and [TxMonth]< monthCriteria) then [ActualQty] else 0),

    InsertedPeriodInCol = Table.AddColumn(
        InsertedBeginQtyCol, 
        "PeriodIn", 
        each if [TxYear] = yearCriteria and [TxMonth] = monthCriteria and [InOutSign] = "+" then [ActualQty] else 0),

    InsertedPeriodOutCol = Table.AddColumn(
        InsertedPeriodInCol, 
        "PeriodOut", 
        each if [TxYear] = yearCriteria and [TxMonth] = monthCriteria and [InOutSign] = "-" then [ActualQty] else 0),

    InsertedEndQtyCol = Table.AddColumn(
        InsertedPeriodOutCol, 
        "EndQty", 
        each [BeginQty]+[PeriodIn]+[PeriodOut]),
    DeletedUnrelated2 = Table.RemoveColumns(
        InsertedEndQtyCol,
        {"Qty", "MovementType", "DocDate", "InOutSign", "TxYear", "TxMonth", "ActualQty"}),
        
    Grouped = Table.Group(
        DeletedUnrelated2, 
        {"MaterialNo"}, 
        {{"期初余额", each List.Sum([BeginQty]), type number}, 
        {"入库数量", each List.Sum([PeriodIn]), type number}, 
        {"出库数量", each List.Sum([PeriodOut]), type number}, 
        {"期末余额", each List.Sum([EndQty]), type number}})
in
    Grouped

将数据上载工作表,在工作表中可以变更年度和月份作为筛选条件,然后在结果表中,右键刷新菜单,对计算结果进行刷新。

示例数据

github

  • stocks.accdb
  • Parameterized Query.xlsx
  • 5
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: SQL Server的参数化查询是一种使用参数来代替查询语句中的具体数值或字符串的查询方式。通常情况下,我们在编写查询语句时,会使用变量来表示查询条件,然后将参数查询语句绑定,最后执行查询。 使用参数化查询的好处有以下几个方面: 1. 提高安全性:通过使用参数,可以避免SQL注入攻击。当我们使用变量传递查询条件时,即使用户输入的值恶意改变查询语句,也不能执行除查询以外的其他操作。 2. 提高性能:由于参数化查询使用了预编译的方式,所以可以减少每次查询的编译开销,提高查询性能。 3. 增强可读性:使用参数可以使查询语句更加清晰易读,增加代码的可维护性。 4. 促进代码复用:由于查询条件是通过参数传递的,所以可以实现代码的复用,减少代码冗余。 在SQL Server中,我们可以使用SqlParameter类来创建参数,并将参数添加到SqlCommand对象中。具体步骤如下: 1. 创建SqlCommand对象:使用参数化查询之前,需要创建一个包含查询语句的SqlCommand对象。 2. 创建SqlParameter对象:使用SqlParameter类的构造函数来创建参数对象,需要指定参数名、参数类型、参数值等属性。 3. 添加参数到SqlCommand对象:通过调用SqlCommand对象的Parameters属性的Add方法,将SqlParameter对象添加到SqlCommand对象中。 4. 执行查询:调用SqlCommand对象的ExecuteReader()方法来执行查询,并获取查询结果。 总之,使用SQL Server的参数化查询可以提高查询的安全性、性能、可读性和代码复用性。当我们需要对数据库进行操作时,尤其是涉及用户输入的查询条件时,建议使用参数化查询来避免潜在的安全风险。 ### 回答2: SQL Server参数化查询是指在执行SQL语句时,将参数作为独立的变量来处理,而不是将参数直接嵌入到SQL语句中。参数化查询可以提高查询的性能和安全性。 首先,参数化查询可以提高查询的性能。当使用参数化查询时,SQL Server可以缓存已编译的查询计划,然后在后续的查询中重用该计划。这样可以减少查询的编译时间,并且避免每次查询都重新编译查询语句,从而提高查询的执行效率。 其次,参数化查询可以提高查询的安全性。通过将参数作为独立的变量处理,可以避免SQL注入攻击。SQL注入是一种常见的安全漏洞,攻击者可以通过在SQL语句中插入恶意代码来执行未经授权的数据库操作。使用参数化查询可以防止攻击者通过注入恶意代码来破坏数据库或获取敏感数据,从而保护数据库的安全性。 此外,参数化查询还可以提高代码的可读性和维护性。通过将参数与SQL语句分离,可以更清晰地理解查询的逻辑和目的。当修改查询时,只需修改参数值,并不需要修改SQL语句的结构,从而减少错误和代码冗余。 总之,SQL Server参数化查询是一种提高查询性能、安全性和代码可读性的好方法。通过将参数作为独立的变量处理,可以减少查询的编译时间、防止SQL注入攻击,并提高代码的可维护性。 ### 回答3: SQL Server参数化查询是一种使用参数来代替实际值的查询方法。参数化查询可以防止SQL注入攻击,并提高查询的性能和安全性。 在SQL Server中,使用参数化查询可以通过声明和设置参数来实现。首先,我们需要声明参数,并指定参数的名称、数据类型和大小。然后,我们可以将参数绑定到查询语句中的相应位置。 参数化查询的好处之一是可以防止SQL注入攻击。SQL注入是指通过在查询语句中插入恶意代码来攻击数据库。使用参数化查询后,查询语句中的参数值是预编译的,不会被解释为可执行的代码,因此可以有效地防止SQL注入攻击。 此外,参数化查询还可以提高查询的性能。在执行查询之前,数据库服务器会对查询进行优化,并创建查询的执行计划。当使用参数化查询时,数据库服务器可以重用生成的执行计划,避免重新编译查询语句,提高查询的执行效率。 参数化查询还可以提高查询的安全性。通过参数化查询,我们可以限制查询的输入值范围,避免不必要的访问和数据泄漏。例如,我们可以在查询中使用参数来限制返回的结果数量或指定特定的条件,从而提供更加安全的查询结果。 总而言之,SQL Server参数化查询是一种有效的查询方法,可以提高查询的性能和安全性。通过声明和设置参数,我们可以防止SQL注入攻击,并优化查询的执行计划,从而提供更高效和安全的数据库查询服务。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值