将非结构化nvarchar数据转换为行和列

时间:2018-03-01 08:50:14

标签: sql json sql-server

我有一个要求,我在数据库中有nvarchar列,我需要从数据库中以行和列的形式提取。

我的nvarchar就像

{
  "notifications": [
    {
      "apMacAddress": "20:50:7a:09:5t:30",
      "associated": false,
      "band": "IEEE_802_11_A",
      "confidenceFactor": 192,
      "deviceId": "34:f4:23:23:f4:wr",
      "entity": "Wired",
      "eventId": 2343434,
      "floorId": 3244234234234,
      "floorRefId": 234235465234,
      "geoCoordinate": {
        "latitude": -999.0,
        "longitude": -999.0,
        "unit": "DEGREES"
      },
      "ipAddress": [
        "23.423.23.234"
      ],
      "lastSeen": "2018-02-09T12:24:53.512+0000",
      "locationCoordinate": {
        "x": 423.2342,
        "y": 23.423,
        "z": 0.0,
        "unit": "FEET"
      },
      "locationMapHierarchy": "North Campus>North SIde>F4>F4 Academies ",
      "manufacturer": "Sony Corporate",
      "maxDetectedRssi": {
        "antennaIndex": 0,
        "apMacAddress": "234:23:56d:45d:5f:20",
        "band": "IEEE_802_11_B",
        "lastHeardInSeconds": 23,
        "rssi": -54,
        "slot": 2
      },
      "notificationType": "locationupdate",
      "rawLocation": {
        "RawX": 0.0,
        "RawY": 0.0,
        "Unit": null
      },
      "ssid": "aide",
      "subscriptionName": " Lab Location",
      "tagVendorData": null,
      "timestamp": 2343657436,
      "username": "abc.gmail.com"
    }
  ]
}

我希望数据像单独的列一样被提取

以下是列名列表

- apMacAddress 
- associated 
- band
- confidenceFactor
- deviceId
- entity
- eventId
- floorId
- floorRefId
- latitude
- longitude
- unit
- ipAddress
- lastSeen
- X
- Y
- Z
- Unit
- locationMapHierarchy
- manufacturer
- antennaIndex
- apMacAddress
- band
- lastHeardInSeconds
- rssi
- slot
- notificationType
- RawX
- RawY
- Unit
- ssid
- subscriptionName
- tagVendorData
- timestamp
- username

有没有办法在SQL中执行此操作?

2 个答案:

答案 0 :(得分:3)

这不是非结构化数据,即JSON数据,一种结构化格式。您无法通过字符串拆分来解析JSON。 SQL Server 2016添加了JSON支持,如Work with JSON data in SQL Server

所示

您可以使用OPENJSON函数解析数据并通过指定其路径来提取属性,例如:

DECLARE @json NVARCHAR(MAX)
SET @json =  
N'{"notifications":[{"apMacAddress":"20:50:7a:09:5t:30","associated":false,"band":"IEEE_802_11_A","confidenceFactor":192,"deviceId":"34:f4:23:23:f4:wr","entity":"Wired","eventId":2343434,"floorId":3244234234234,"floorRefId":234235465234,"geoCoordinate":{"latitude":-999.0,"longitude":-999.0,"unit":"DEGREES"},"ipAddress":["23.423.23.234"],"lastSeen":"2018-02-09T12:24:53.512+0000","locationCoordinate":{"x":423.2342,"y":23.423,"z":0.0,"unit":"FEET"},"locationMapHierarchy":"North Campus>North SIde>F4>F4 Academies ","manufacturer":"Sony Corporate","maxDetectedRssi":{"antennaIndex":0,"apMacAddress":"234:23:56d:45d:5f:20","band":"IEEE_802_11_B","lastHeardInSeconds":23,"rssi":-54,"slot":2},"notificationType":"locationupdate","rawLocation":{"RawX":0.0,"RawY":0.0,"Unit":null},"ssid":"aide","subscriptionName":" Lab Location","tagVendorData":null,"timestamp":2343657436,"username":"abc.gmail.com"}]}'  

SELECT *  
FROM OPENJSON(@json, '$.notifications')
    with (
        apMacAddress nvarchar(500) '$.apMacAddress',
        associated bit '$.associated',
        band nvarchar(50) '$.band'
    )

这将返回:

apMacAddress        associated  band
20:50:7a:09:5t:30   0           IEEE_802_11_A

要解析嵌套结构,您需要使用AS JSON子句提取结构,并使用CROSS APPLY再次对其应用OPENJSON,例如:

SELECT apMacAddress,associated,band,latitude,longitude  
FROM OPENJSON(@json, '$.notifications')
    with (
        apMacAddress nvarchar(500) '$.apMacAddress',
        associated bit '$.associated',
        band nvarchar(50) '$.band',
        geoCoordinate nvarchar(max) AS JSON
    )
    CROSS APPLY OPENJSON(geoCoordinate) with (
        latitude decimal(5,2) '$.latitude',
        longitude decimal(5,2) '$.longitude'
    )

将返回:

apMacAddress        associated  band            latitude    longitude
20:50:7a:09:5t:30   0           IEEE_802_11_A   -999.00     -999.00

在早期版本中,您必须在将JSON字符串插入数据库之前解析它们,例如使用JSON.NET,这是.NET最流行的JSON解析器。

虽然早期版本有一些JSON解析器实现,例如this one,但它们很复杂,速度慢且难以使用

更新

要清楚,OPENJSON是一个像任何其他表值函数一样的函数,例如STRING_SPLIT。这意味着它可以使用CROSS APPLY

应用于字段
SELECT *  
from SomeTable
CROSS APPLY OPENJSON(SomeTable.MyJsonField, '$.notifications')
with (
        apMacAddress nvarchar(500) '$.apMacAddress',
        associated bit '$.associated',
        band nvarchar(50) '$.band',
        geoCoordinate nvarchar(max) AS JSON
    )
CROSS APPLY OPENJSON(geoCoordinate) 
with (
        latitude decimal(5,2) '$.latitude',
        longitude decimal(5,2) '$.longitude'
)

要提取更多嵌套结构,必须在顶部提取它们,然后使用OPENJSON解析,就像geoCoordinate一样:

SELECT apMacAddress,associated,band,latitude,longitude ,x,y
FROM OPENJSON(@json, '$.notifications')
    with (
        apMacAddress nvarchar(500) '$.apMacAddress',
        associated bit '$.associated',
        band nvarchar(50) '$.band',
        geoCoordinate nvarchar(max) AS JSON,
        locationCoordinate nvarchar(max) AS JSON
    )
    CROSS APPLY OPENJSON(geoCoordinate) 
    with (
        latitude decimal(5,2) '$.latitude',
        longitude decimal(5,2) '$.longitude'
    )
    CROSS APPLY OPENJSON(locationCoordinate) with (
        x decimal(5,2) '$.x',
        y decimal(5,2) '$.y'
)

将返回locationCoordinate.x和locationCoordinate.y值:

apMacAddress        associated  band            latitude    longitude   x   y
20:50:7a:09:5t:30   0           IEEE_802_11_A   -999.00     -999.00   423.23    23.42

答案 1 :(得分:1)

除了Panagiotis Kanavos回复之外,假设您正在使用SQL Server 2016及更高版本(兼容级别130),您可以使用OPENJSON函数。

另一个假设是你的Json结构将改变 您可以使用以下内容将元素解析为列

<强> UPDATE(!!)

    SET NOCOUNT ON 
    IF OBJECT_ID ('tempdb..##DataSet') IS NOT NULL DROP TABLE ##DataSet

    DECLARE @Table TABLE (Id int identity (1,1) , JsonCol NVARCHAR(MAX))

    INSERT INTO @Table (JsonCol)
    VALUES ('{
      "notifications": [
        {
          "apMacAddress": "20:50:7a:09:5t:30",
          "associated": false,
          "band": "IEEE_802_11_A",
          "confidenceFactor": 192,
          "deviceId": "34:f4:23:23:f4:wr",
          "entity": "Wired",
          "eventId": 2343434,
          "floorId": 3244234234234,
          "floorRefId": 234235465234,
          "geoCoordinate": {
            "latitude": -888.0,
            "longitude": -888.0,
            "unit": "DEGREES"
          },
          "ipAddress": [
            "23.423.23.234"
          ],
          "lastSeen": "2018-02-09T12:24:53.512+0000",
          "locationCoordinate": {
            "x": 423.2342,
            "y": 23.423,
            "z": 0.0,
            "unit": "FEET"
          },
          "locationMapHierarchy": "North Campus>North SIde>F4>F4 Academies ",
          "manufacturer": "Sony Corporate",
          "maxDetectedRssi": {
            "antennaIndex": 0,
            "apMacAddress": "234:23:56d:45d:5f:20",
            "band": "IEEE_802_11_B",
            "lastHeardInSeconds": 23,
            "rssi": -54,
            "slot": 2
          },
          "notificationType": "locationupdate",
          "rawLocation": {
            "RawX": 0.0,
            "RawY": 0.0,
            "Unit": null
          },
          "ssid": "aide",
          "subscriptionName": " Lab Location",
          "tagVendorData": null,
          "timestamp": 2343657436,
          "username": "abc.gmail.com"
        }
      ]
    }'),
    ('{
      "notifications": [
        {
          "apMacAddress": "20:50:7a:09:5t:30",
          "associated": false,
          "band": "IEEE_802_11_A",
          "confidenceFactor": 192,
          "deviceId": "34:f4:23:23:f4:wr",
          "entity": "Wired",
          "eventId": 2343434,
          "floorId": 3244234234234,
          "floorRefId": 234235465234,
          "geoCoordinate": {
            "latitude": -999.0,
            "longitude": -999.0,
            "unit": "DEGREES"
          },
          "ipAddress": [
            "23.423.23.234"
          ],
          "lastSeen": "2018-02-09T12:24:53.512+0000",
          "locationCoordinate": {
            "x": 423.2342,
            "y": 23.423,
            "z": 0.0,
            "unit": "FEET"
          },
          "locationMapHierarchy": "North Campus>North SIde>F4>F4 Academies ",
          "manufacturer": "Sony Corporate",
          "maxDetectedRssi": {
            "antennaIndex": 0,
            "apMacAddress": "234:23:56d:45d:5f:20",
            "band": "IEEE_802_11_B",
            "lastHeardInSeconds": 23,
            "rssi": -54,
            "slot": 2
          },
          "notificationType": "locationupdate",
          "rawLocation": {
            "RawX": 0.0,
            "RawY": 0.0,
            "Unit": null
          },
          "ssid": "aide",
          "subscriptionName": " Lab Location",
          "tagVendorData": null,
          "timestamp": 2343657436,
          "username": "abc.gmail.com"
        }
      ]
    }')
    ,(
    '{
      "notifications": [
        {
          "apMacAddress": "20:50:7a:09:5t:30",
          "associated": false,
          "band": "IEEE_802_11_A",
          "confidenceFactor": 192,
          "deviceId": "34:f4:23:23:f4:wr",
          "entity": "Wired",
          "eventId": 2343434,
          "floorId": 3244234234234,
          "floorRefId": 234235465234,
          "geoCoordinate": {
            "latitude": -777.0,
            "longitude": -777.0,
            "unit": "DEGREES"
          },
          "ipAddress": [
            "23.423.23.234"
          ],
          "lastSeen": "2018-02-09T12:24:53.512+0000",
          "locationCoordinate": {
            "x": 423.2342,
            "y": 23.423,
            "z": 0.0,
            "unit": "FEET"
          },
          "locationMapHierarchy": "North Campus>North SIde>F4>F4 Academies ",
          "manufacturer": "Sony Corporate",
          "maxDetectedRssi": {
            "antennaIndex": 0,
            "apMacAddress": "234:23:56d:45d:5f:20",
            "band": "IEEE_802_11_B",
            "lastHeardInSeconds": 23,
            "rssi": -54,
            "slot": 2
          },
          "notificationType": "locationupdate",
          "rawLocation": {
            "RawX": 0.0,
            "RawY": 0.0,
            "Unit": null
          },
          "ssid": "aide",
          "subscriptionName": " Lab Location",
          "tagVendorData": null,
          "timestamp": 2343657436,
          "username": "abc.gmail.com"
        }
      ]
    }'



    )

    ;WITH  BrkJson as 
    (
    SELECT * 
    FROM @Table
    CROSS APPLY  OPENJSON (JsonCol, '$.notifications[0]') R
    )


    ,Dataset as 
    (
    SELECT Id, [key] , [value]
    FROM BrkJson 
    WHERE [type] != 5 /*Array*/ 
    UNION ALL
    SELECT b.Id ,b.[Key] +'_'+ t.[key] [key] , t.[value]
    FROM BrkJson  b
        CROSS APPLY OPENJSON (value) t
    WHERE b.[type] = 5 
    )



    SELECT *
    INTO ##DataSet 
    FROM Dataset


    DECLARE @cols NVARCHAR(MAX)=''
    DECLARE @pvt NVARCHAR(MAX)

    SELECT @cols +=','+ QUOTENAME([key])
    FROM ##DataSet
    GROUP BY [key]

    SET @cols = STUFF(@cols,1,1,'')
    --PRINT @cols

    SET @pvt = 
    'SELECT * 
     FROM ##DataSet 
        PIVOT 
            (
            MAX([value]) FOR  [key] in ('+@cols+')
            ) e
    '
    EXEC sp_executesql @Pvt 
    DROP TABLE ##DataSet