

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 连接到 Microsoft SQL Server 数据来源
<a name="using-microsoft-sql-server-in-AMG"></a>

 使用 Microsoft SQL Server（MSSQL）数据来源查询和可视化来自任何 Microsoft SQL Server 2005 或更高版本（包括 Microsoft Azure SQL 数据库）的数据。

**重要**  
Grafana 版本 8.0 更改了 Microsoft SQL Server、Postgres 和 MySQL 数据帧的底层数据结构。因此，时间序列查询结果将以宽格式返回。有关更多信息，请参阅 Grafana 数据帧文档中的[宽格式](https://grafana.com/developers/plugin-tools/introduction/data-frames#wide-format)。  
要使可视化效果和以前一样，您可能需要进行一些手动迁移。Github 上记录了一个解决方案，网址为 [Postgres/MySQL/MSSQL：v8.0 中与时间序列查询和数据列排序相关的重大更改](https://github.com/grafana/grafana/issues/35534)。

## 添加数据来源
<a name="mssql-adding-the-data-source"></a>

1.  选择顶部标题中的 Grafana 图标，打开侧边菜单。

1.  在**配置**链接下的侧边菜单中，您应该可以找到**数据来源**链接。

1.  选择顶部标题中的 **\$1 添加数据来源**按钮。

1.  从**类型**下拉列表中选择 **Microsoft SQL Server**。

### 数据来源选项
<a name="mssql-data-source-options"></a>


|  Name  |  说明  | 
| --- | --- | 
|  Name  |  数据来源名称。您将在面板和查询中通过其名称查看数据来源。 | 
|  Default  |  默认数据来源意味着将为新面板预先选择该数据来源。 | 
|  Host  |  您的 MSSQL 实例的 IP address/hostname 和可选端口。如果省略端口，则将使用默认的 1433。 | 
|  Database  |  MSSQL 数据库的名称。 | 
|  User  |  数据库用户的登录名/用户名。 | 
|  Password  |  数据库用户密码。 | 
|  Encrypt  |  此选项决定是否或在多大程度上与服务器协商安全 SSL TCP/IP 连接，默认false（Grafana v5.4\$1）。 | 
|  Max open  |  数据库的最大打开连接数，默认为 unlimited（Grafana v5.4\$1）。 | 
|  Max idle  |  空闲连接池中的最大连接数，默认为 2（Grafana v5.4\$1）。 | 
|  Max lifetime  |  连接可重复使用的最长时间（秒），默认为 14400/4 小时。 | 

### 最小时间间隔
<a name="mssql-min-time-interval"></a>

 `$_interval` `$_interval_ms` 变量的下限。建议设置以写入频率，例如，如果您的数据每分钟写入一次，则为 `1m`。此选项也可以 overridden/configured 位于仪表板面板的数据源选项下。此值的格式**必须**为数字，后跟有效的时间标识符；例如，`1m`（1 分钟）或 `30s`（30 秒）。支持以下时间标识符。


|  标识符  |  说明  | 
| --- | --- | 
|  y  |  Year  | 
|  M  |  Month  | 
|  w  |  周  | 
|  d  |  天  | 
|  h  |  小时  | 
|  m  |  分钟  | 
|  s  |  秒  | 
|  ms  |  毫秒  | 

### 数据库用户权限
<a name="mssql-database-user-permissions-important"></a>

**重要**  
 添加数据来源时指定的数据库用户只能获得对要查询的指定数据库和表的 SELECT 权限。Grafana 不会验证查询是否安全。查询可以包含任何 SQL 语句。例如，可以运行 `DELETE FROM user;` 和 `DROP TABLE user;` 之类的语句。为了防止这种情况，强烈建议您创建具有受限权限的特定 MSSQL 用户。

 以下示例代码显示了如何创建具有受限权限的特定 MSSQL 用户。

```
 CREATE USER grafanareader WITH PASSWORD 'password'
 GRANT SELECT ON dbo.YourTable3 TO grafanareader
```

 确保用户没有从公有角色获得任何不需要的权限。

### 已知问题
<a name="mssql-known-issues"></a>

 如果您使用的是旧版本的 Microsoft SQL Server，比如 2008 和 2008R2，则可能需要禁用加密才能连接。如果可能，建议您使用最新的服务包，以获得最佳兼容性。

## 查询编辑器
<a name="mssql-query-editor"></a>

 在图形、Singlestat 或表面板编辑模式的“指标”选项卡中，您可以找到 MSSQL 查询编辑器。要进入编辑模式，请选择面板标题，然后选择“编辑”。您可以使用编辑器定义 SQL 查询以选择要可视化的数据。

1.  选择*格式化为* `Time series`（用于图形或 Singlestat 面板等）或 `Table`（用于表面板等）。

1.  这是您编写 SQL 查询的实际编辑器。

1.  在查询编辑器下方显示 MSSQL 的帮助部分。

1.  显示运行的 SQL 查询。在成功运行查询后最先可用。

1.  添加一个额外的查询，其中显示额外的查询编辑器。

## 宏
<a name="mssql-macros"></a>

 为了简化语法并允许动态部分（如日期范围筛选器），查询可包含宏。


|  宏示例  |  说明  | 
| --- | --- | 
|  \$1\$1\$1time(dateColumn)  |  将替换为表达式，以将列重命名为 time。例如，dateColumn as time。 | 
|  \$1\$1\$1timeEpoch(dateColumn)  |  将替换为表达式，将 DATETIME 列类型转换为 Unix 时间戳，并将其重命名为 time。例如，DATEDIFF(second, "1970-01-01", dateColumn) AS time。 | 
|  \$1\$1\$1timeFilter(dateColumn)  |  将替换为使用指定列名的时间范围筛选条件。例如，dateColumn BETWEEN "2017-04-21T05:01:17Z" AND "2017-04-21T05:06:17Z"。 | 
|  \$1\$1\$1timeFrom()  |  将替换为当前活动时间选择的开始时间。例如，"2017-04-21T05:01:17Z"。 | 
|  \$1\$1\$1timeTo()  |  将替换为当前活动时间选择的结束时间。例如，"2017-04-21T05:06:17Z"。 | 
|  \$1\$1\$1timeGroup(dateColumn,'5m'[, fillvalue])  |  将替换为 GROUP BY 子句中可用的表达式。如果 fillValue 为 NULL 或浮点值，则会自动用该值填充时间范围内的空序列。例如，CAST(ROUND(DATEDIFF(second, "1970-01-01", time\$1column)/300.0, 0) as bigint)\$1300。 | 
|  \$1\$1\$1timeGroup(dateColumn,'5m', 0)  |  与前面相同，但带有填充参数，序列中缺失的点将由 Grafana 添加，0 将用作值。 | 
|  \$1\$1\$1timeGroup(dateColumn,'5m', NULL)  |  与上面相同，但 NULL 将用作缺失点的值。 | 
|  \$1\$1\$1timeGroup(dateColumn,'5m', previous)  |  与上面相同，但如果未看到任何值，则该序列中的前一个值将用作填充值，但使用 NULL（仅适用于 Grafana 5.3\$1）。 | 

 在面板编辑模式下，查询编辑器有一个**生成式 SQL** 链接，并在查询运行后显示。选择之后，将会展开并显示运行的原始插值 SQL 字符串。

## 表查询
<a name="mssql-table-queries"></a>

 如果查询选项设置为**格式化为****表**，则基本上可以执行任何类型的 SQL 查询。表面板将自动显示查询返回的任何列和行的结果。

 以下示例代码显示了一个数据库表。

```
CREATE TABLE [event] (
  time_sec bigint,
  description nvarchar(100),
  tags nvarchar(100),
)
```

```
CREATE TABLE [mssql_types] (
  c_bit bit, c_tinyint tinyint, c_smallint smallint, c_int int, c_bigint bigint, c_money money, c_smallmoney smallmoney, c_numeric numeric(10,5),
  c_real real, c_decimal decimal(10,2), c_float float,
  c_char char(10), c_varchar varchar(10), c_text text,
  c_nchar nchar(12), c_nvarchar nvarchar(12), c_ntext ntext,
  c_datetime datetime,  c_datetime2 datetime2, c_smalldatetime smalldatetime, c_date date, c_time time, c_datetimeoffset datetimeoffset
)

INSERT INTO [mssql_types]
SELECT
  1, 5, 20020, 980300, 1420070400, '$20000.15', '£2.15', 12345.12,
  1.11, 2.22, 3.33,
  'char10', 'varchar10', 'text',
  N'☺nchar12☺', N'☺nvarchar12☺', N'☺text☺',
  GETDATE(), CAST(GETDATE() AS DATETIME2), CAST(GETDATE() AS SMALLDATETIME), CAST(GETDATE() AS DATE), CAST(GETDATE() AS TIME), SWITCHOFFSET(CAST(GETDATE() AS DATETIMEOFFSET), '-07:00')
```

 以下示例代码显示了一个查询。

```
SELECT * FROM [mssql_types]
```

 您可以使用常规 `AS` SQL 列选择语法来控制表面板列的名称，如以下示例代码所示。

```
SELECT
  c_bit as [column1], c_tinyint as [column2]
FROM
  [mssql_types]
```

 生成的表面板：

## 时间序列查询
<a name="mssql-time-series-queries"></a>

 如果将**格式化为**设置为**时间序列**，例如在图形面板中使用，查询必须有一个名为 `time` 的列，该列返回 SQL 日期时间或任何表示 Unix 纪元（秒）的数字数据类型。您可以返回一个名为 `metric` 的列，该列用作值列的指标名称。除 `time` 和 `metric` 之外的任何列都被视为值列。如果省略 `metric` 列，则值列的名称为指标名称。您可以选择多个值列，每个值列都将使用其名称作为指标。如果返回多个值列和一个名为的 `metric` 列，则此列将用作序列名称的前缀。

 时间序列查询的结果集必须按时间排序。

 以下示例代码显示了一个数据库表。

```
CREATE TABLE [event] (
  time_sec bigint,
  description nvarchar(100),
  tags nvarchar(100),
)
```

```
CREATE TABLE metric_values (
  time datetime,
  measurement nvarchar(100),
  valueOne int,
  valueTwo int,
)

INSERT metric_values (time, measurement, valueOne, valueTwo) VALUES('2018-03-15 12:30:00', 'Metric A', 62, 6)
INSERT metric_values (time, measurement, valueOne, valueTwo) VALUES('2018-03-15 12:30:00', 'Metric B', 49, 11)
...
INSERT metric_values (time, measurement, valueOne, valueTwo) VALUES('2018-03-15 13:55:00', 'Metric A', 14, 25)
INSERT metric_values (time, measurement, valueOne, valueTwo) VALUES('2018-03-15 13:55:00', 'Metric B', 48, 10)
```

 以下示例代码显示了一个 `value` 列和一个 `metric` 列。

```
SELECT
  time,
  valueOne,
  measurement as metric
FROM
  metric_values
WHERE
  $__timeFilter(time)
ORDER BY 1
```

 在图形面板中使用上述查询时，将生成两个名为 `Metric A` 和 `Metric B` 的序列，其值 `valueOne` 和 `valueTwo` 绘制在 `time` 上。

 以下示例代码显示了多个 `value` 列。

```
SELECT
  time,
  valueOne,
  valueTwo
FROM
  metric_values
WHERE
  $__timeFilter(time)
ORDER BY 1
```

 在图形面板中使用上述查询时，将生成两个名为 `Metric A` 和 `Metric B` 的序列，其值 `valueOne` 和 `valueTwo` 绘制在 `time` 上。

 以下示例代码演示了如何使用 \$1\$1\$1TimeGroup 宏。

```
SELECT
  $__timeGroup(time, '3m') as time,
  measurement as metric,
  avg(valueOne)
FROM
  metric_values
WHERE
  $__timeFilter(time)
GROUP BY
  $__timeGroup(time, '3m'),
  measurement
ORDER BY 1
```

 在图形面板中使用上述查询时，将生成两个名为 `Metric A` 和 `Metric B` 的序列，其值 `valueOne` 和 `valueTwo` 绘制在 `time` 上。在三分钟的窗口中，任何两个序列缺少一个值，都会在这两条线之间呈现一条线。您会注意到，右侧的图形永远不会下降到零。

 以下示例代码演示了如何使用 \$1\$1\$1TimeGroup 宏并将填充参数设置为零。

```
SELECT
  $__timeGroup(time, '3m', 0) as time,
  measurement as metric,
  sum(valueTwo)
FROM
  metric_values
WHERE
  $__timeFilter(time)
GROUP BY
  $__timeGroup(time, '3m'),
  measurement
ORDER BY 1
```

 在图形面板中使用此查询时，结果是两个名为 `Metric A` 和 `Metric B` 的序列，其中 `valueTwo` 的总和绘制在 `time` 上。在三分钟窗口内缺少值的任何序列都将具有零值，可在右侧的图形中看到此值。

## 模板化
<a name="mssql-templating"></a>

 您可以在指标查询中使用变量来代替服务器、应用程序和传感器名称等硬编码。变量显示为控制面板顶部的下拉选择框。您可以使用这些下拉框来更改控制面板中显示的数据。

 有关模板化和模板变量的更多信息，请参阅 [模板和变量](templates-and-variables.md)。

### 查询变量
<a name="mssql-query-variable"></a>

 如果添加 `Query` 类型的模板变量，则可以编写一个 MSSQL 查询，该查询可以返回测量名称、键名或键值等显示为下拉选择框的内容。

 例如，如果在模板变量 *Query* 设置中指定了这样的查询，则会有一个变量，其中包含表中 `hostname` 列的所有值的。

```
SELECT hostname FROM host
```

 查询可以返回多列，Grafana 会自动根据这些列创建一个列表。例如，以下查询将返回一个列表，其中包含来自 `hostname` 和 `hostname2` 的值。

```
SELECT [host].[hostname], [other_host].[hostname2] FROM host JOIN other_host ON [host].[city] = [other_host].[city]
```

 另一种选择是可以创建 key/value 变量的查询。该查询应返回名为 `__text` 和 `__value` 的两列。`__text` 列值应该是唯一的（如果它不是唯一的，则使用第一个值）。下拉列表中的选项包含文本和值，允许将友好名称作为文本，将 id 作为值。以 `hostname` 作为文本，以 `id` 作为值的示例查询：

```
SELECT hostname __text, id __value FROM host
```

 您还可以创建嵌套变量。例如，您还有一个名为 `region` 的变量。然后，您可以让主机变量仅显示来自当前所选区域的主机，并使用以下查询（如果 `region` 是多值变量，则使用 `IN` 比较运算符而不是 `=` 与多个值匹配）。

```
SELECT hostname FROM host WHERE region IN ($region)
```

### 在查询中使用变量
<a name="mssql-using-variables-in-queries"></a>

**注意**  
 仅当模板变量是 `multi-value` 时，才会引用模板变量值。

 如果该变量是多值变量，则使用 `IN` 比较运算符而不是 `=` 与多个值匹配。

 共有两种语法：

 `$<varname>` 名为 `hostname` 的模板变量的示例：

```
SELECT
  atimestamp time,
  aint value
FROM table
WHERE $__timeFilter(atimestamp) and hostname in($hostname)
ORDER BY atimestamp
```

 `[[varname]]` 名为 `hostname` 的模板变量的示例：

```
SELECT
  atimestamp as time,
  aint as value
FROM table
WHERE $__timeFilter(atimestamp) and hostname in([[hostname]])
ORDER BY atimestamp
```

#### 关闭多值变量的引用
<a name="mssql-disabling-quoting-for-multi-value-variables"></a>

 Grafana 会自动为多值变量创建带引号、以逗号分隔的字符串。例如，如果选择 `server01` 和 `server02`，则其格式为：`'server01', 'server02'`。要关闭引用，请对变量使用 csv 格式选项。

 `${servers:csv}` 

 有关变量格式选项的更多信息，请参阅 [模板和变量](templates-and-variables.md)。

## Annotations
<a name="mssql-annotations"></a>

 您可以使用注释在图形上叠加丰富的事件信息。您可以通过控制面板菜单/注释视图添加注释查询。有关更多信息，请参阅 [Annotations](dashboard-annotations.md)。

 **列：**


|  Name  |  说明  | 
| --- | --- | 
|  time  |  date/time 字段的名称。可以是具有原生 SQL date/time 数据类型或纪元值的列。 | 
|  timeend  |  结束 date/time 字段的可选名称。可以是具有原生 SQL date/time 数据类型或纪元值的列。 | 
|  text  |  事件描述字段。 | 
|  tags  |  用于事件标签的可选字段名称，显示为以逗号分隔的字符串。 | 

 以下示例代码显示了数据库表。

```
CREATE TABLE [events] (
  time_sec bigint,
  description nvarchar(100),
  tags nvarchar(100),
)
```

 我们还使用 [时间序列查询](#mssql-time-series-queries) 中定义的数据库表。

 以下示例代码演示了一个查询，该查询使用具有纪元值的 time 列。

```
SELECT
  time_sec as time,
  description as [text],
  tags
FROM
  [events]
WHERE
  $__unixEpochFilter(time_sec)
ORDER BY 1
```

 以下示例代码演示了一个区域查询，该查询使用具有纪元值的 time 和 timeend 列。

```
SELECT
  time_sec as time,
  time_end_sec as timeend,
  description as [text],
  tags
FROM
  [events]
WHERE
  $__unixEpochFilter(time_sec)
ORDER BY 1
```

 以下示例代码显示了使用原生 SQL date/time 数据类型的时间列的查询。

```
SELECT
  time,
  measurement as text,
  convert(varchar, valueOne) + ',' + convert(varchar, valueTwo) as tags
FROM
  metric_values
WHERE
  $__timeFilter(time_column)
ORDER BY 1
```

## 存储过程支持
<a name="stored-procedure-support"></a>

 存储过程已经过验证，可以正常工作。但在某些边缘情况下，可能无法按您的预期工作。表、时间序列和注释查询应支持存储过程，前提是您使用与前面各节所述相同的列命名，并以相同的格式返回数据。

 宏函数在存储过程中不起作用。

### 示例
<a name="mssql-examples"></a>

 对于以下示例，数据库表是在时间序列查询中定义的。假设您要在图形面板中可视化四个序列，例如 `valueOne`、`valueTwo` 和 `measurement` 列的任意组合。右侧的图形面板直观地展示了我们想要实现的目标。要解决此问题，您必须使用两个查询：

 以下示例代码显示了第一个查询。

```
SELECT
  $__timeGroup(time, '5m') as time,
  measurement + ' - value one' as metric,
  avg(valueOne) as valueOne
FROM
  metric_values
WHERE
  $__timeFilter(time)
GROUP BY
  $__timeGroup(time, '5m'),
  measurement
ORDER BY 1
```

 以下示例代码显示了第二个查询。

```
SELECT
  $__timeGroup(time, '5m') as time,
  measurement + ' - value two' as metric,
  avg(valueTwo) as valueTwo
FROM
  metric_values
GROUP BY
  $__timeGroup(time, '5m'),
  measurement
ORDER BY 1
```

#### 使用纪元格式时间的存储过程
<a name="mssql-stored-procedure-using-time-in-epoch-format"></a>

 您可以定义一个存储过程，该过程将返回在图形面板中呈现四个序列所需的所有数据，如上所示。在此情况下，存储过程接受两个参数 `@from` 和 `@to`，数据类型为 `int`，这应该是纪元格式的时间范围（from-to），用于筛选从存储过程返回的数据。

 这模仿了 select 和 group by 表达式中的 `$__timeGroup(time, '5m')`，这就是为什么需要大量冗长的表达式。如果需要，可将其提取到 MSSQL 函数中。

```
CREATE PROCEDURE sp_test_epoch(
  @from int,
  @to   int
)   AS
BEGIN
  SELECT
    cast(cast(DATEDIFF(second, {d '1970-01-01'}, DATEADD(second, DATEDIFF(second,GETDATE(),GETUTCDATE()), time))/600 as int)*600 as int) as time,
    measurement + ' - value one' as metric,
    avg(valueOne) as value
  FROM
    metric_values
  WHERE
    time >= DATEADD(s, @from, '1970-01-01') AND time <= DATEADD(s, @to, '1970-01-01')
  GROUP BY
    cast(cast(DATEDIFF(second, {d '1970-01-01'}, DATEADD(second, DATEDIFF(second,GETDATE(),GETUTCDATE()), time))/600 as int)*600 as int),
    measurement
  UNION ALL
  SELECT
    cast(cast(DATEDIFF(second, {d '1970-01-01'}, DATEADD(second, DATEDIFF(second,GETDATE(),GETUTCDATE()), time))/600 as int)*600 as int) as time,
    measurement + ' - value two' as metric,
    avg(valueTwo) as value
  FROM
    metric_values
  WHERE
    time >= DATEADD(s, @from, '1970-01-01') AND time <= DATEADD(s, @to, '1970-01-01')
  GROUP BY
    cast(cast(DATEDIFF(second, {d '1970-01-01'}, DATEADD(second, DATEDIFF(second,GETDATE(),GETUTCDATE()), time))/600 as int)*600 as int),
    measurement
  ORDER BY 1
END
```

 然后，您可以对图形面板使用以下查询。

```
DECLARE
  @from int = $__unixEpochFrom(),
  @to int = $__unixEpochTo()

EXEC dbo.sp_test_epoch @from, @to
```

#### 使用日期时间格式的时间存储过程
<a name="mssql-stored-procedure-using-time-in-datetime-format"></a>

 您可以定义一个存储过程，该过程将返回在图形面板中呈现四个序列所需的所有数据，如上所示。在此情况下，存储过程接受两个参数 `@from` 和 `@to`，数据类型为 `datetime`，这应该是时间范围（from-to），用于筛选从存储过程返回的数据。

 这模仿了 select 和 group by 表达式中的 `$__timeGroup(time, '5m')`，这就是为什么需要大量冗长的表达式。如果需要，可将其提取到 MSSQL 函数中。

```
CREATE PROCEDURE sp_test_datetime(
  @from datetime,
  @to   datetime
)   AS
BEGIN
  SELECT
    cast(cast(DATEDIFF(second, {d '1970-01-01'}, time)/600 as int)*600 as int) as time,
    measurement + ' - value one' as metric,
    avg(valueOne) as value
  FROM
    metric_values
  WHERE
    time >= @from AND time <= @to
  GROUP BY
    cast(cast(DATEDIFF(second, {d '1970-01-01'}, time)/600 as int)*600 as int),
    measurement
  UNION ALL
  SELECT
    cast(cast(DATEDIFF(second, {d '1970-01-01'}, time)/600 as int)*600 as int) as time,
    measurement + ' - value two' as metric,
    avg(valueTwo) as value
  FROM
    metric_values
  WHERE
    time >= @from AND time <= @to
  GROUP BY
    cast(cast(DATEDIFF(second, {d '1970-01-01'}, time)/600 as int)*600 as int),
    measurement
  ORDER BY 1
END
```

 然后，您可以对图形面板使用以下查询。

```
DECLARE
  @from datetime = $__timeFrom(),
  @to datetime = $__timeTo()

EXEC dbo.sp_test_datetime @from, @to
```

## 警报
<a name="mssql-alerting"></a>

 时间序列查询应在警报条件下工作。警报规则条件尚不支持表格式查询。