Проблемы SQL с полями DateFields при использовании FOR XML (для создания строки JSON) - PullRequest
1 голос
/ 07 ноября 2019

Основываясь на этом ответе , я создал функцию для циклического перебора имен столбцов и возврата результата запроса в формат JSON. Я использую FOR XML, потому что я работаю над версией SQL Server, которая не поддерживает FOR JSON.

У меня есть запрос, возвращающий допустимый формат JSON, за исключением случаев, когда есть поля даты, и хотя я искал в ИнтернетеЯ думаю, что я немного над головой с точки зрения глубины понимания SQL Server. Я играл с CAST и CONVERT и ISDATE, но не могу получить запрос для получения результатов. Мне также немного непонятно, как работает value('.', 'varchar(max)'), хотя я уже прочитал его.

Например, этот запрос отлично работает.

SELECT
    STUFF((
        SELECT',{"account_no":"' + account_no + '"' + ',"version_num":"' + version_num + '"' + ',"user_id":"' + user_id + '"' + '}' 
        FROM uAccountHighLevel
        WHERE account_no='3718035' and version_num='37' 
        FOR XML path(''), type).value('.', 'varchar(max)')
    , 1, 1, '')

Возвращение

{"account_no":"3718035","version_num":"37","user_id":"Sholtzman"}

Однако, когда я добавляю поле datetime (или date) в запрос, происходит следующее сообщение:

Conversion failed when converting date and/or time from character string.

См. Запрос с добавленным полем datetime:

SELECT
    STUFF((
        SELECT',{"account_no":"' + account_no + '"' + ',"version_num":"' + version_num + '"' + ',"user_id":"' + user_id + '"' + ',"time_stamp":"' + time_stamp + '"' + '}' 
        FROM uAccountHighLevel 
        WHERE account_no='3718035' and version_num='37' 
        FOR XML path(''), type).value('.', 'varchar(max)')
    , 1, 1, '')

Вот снимок таблицы, с которой я работаю. Все таблицы имеют одинаковое сочетание типов данных:

enter image description here

Если бы значения полей были известны каждый раз, думаю, я мог бы решить это, но функцияэто создает этот запрос, будет многократно использоваться во многих таблицах, поэтому поле даты / даты и времени может появляться в любом порядке, поэтому мне нужна эта функция, чтобы каким-то образом создать для обработки полей даты и времени. У меня также нет контроля над изменением типов данных в базе данных.

Ниже приведен код vba, который я использую для создания JSON.

Sub querySQL()

    Dim conn As ADODB.Connection
    Set conn = New ADODB.Connection
    conn.Open "Provider=SQLOLEDB;Data Source=DVW-SQL02;Initial Catalog=UniversalQuoteProposal;UID=SVC_UQP;PWD=$vc13#up"

    'grab column names
    Dim sql As String
    sql = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = N'uAccountHighLevel'"

    Dim rs As ADODB.Recordset
    Set rs = New ADODB.Recordset

    With rs
        .ActiveConnection = conn
        .CursorLocation = adUseClient
        .Open sql, conn, adOpenKeyset, adLockReadOnly, adCmdText
    End With

    sql = buildSQL("uAccountHighLevel", rs, "List")

    Dim rsFinal As ADODB.Recordset
    Set rsFinal = New ADODB.Recordset

    With rsFinal
        .ActiveConnection = conn
        .CursorLocation = adUseClient
        .Open sql, conn, adOpenKeyset, adLockReadOnly, adCmdText
        Debug.Print .Fields(0).Value
    End With


End Sub

Function buildSQL(theTable As String, rs As ADODB.Recordset, arrayOrList As String) As String

    'this has to be fixed, but we can do it :)
    'adjust query builder and also had a case for isdate to cast to string

    Dim queryBuilder As String
    queryBuilder = "SELECT "
    If arrayOrList = "array" Then queryBuilder = queryBuilder & "'[' + "
    queryBuilder = queryBuilder & "STUFF(("
    queryBuilder = queryBuilder & "SELECT',{"

    rs.MoveFirst
    Dim f As Long
    For f = 0 To 3 'rs.RecordCount - 1
        queryBuilder = queryBuilder & """" & rs.Fields(0).Value & """:""' + " & rs.Fields(0).Value & " + '""' + ',"
        'this is one of my attempts to play with isdate, cast, convert
        'queryBuilder = queryBuilder & """" & rs.Fields(0).Value & """:""' + (CASE WHEN ISDATE(" & rs.Fields(0).Value & ") = 1 THEN CONVERT(datetime, cast([" & rs.Fields(0) & "] AS CHAR(8))) END) + '""' + ',"
        rs.MoveNext
    Next

    queryBuilder = Left(queryBuilder, Len(queryBuilder) - 1) & "}'"

    queryBuilder = queryBuilder & " FROM " & theTable & " WHERE account_no='3718035' and version_num='37' "
    queryBuilder = queryBuilder & " for xml path(''), type"
    queryBuilder = queryBuilder & ").value('.', 'varchar(max)'), 1, 1, '')"

    If arrayOrList = "array" Then queryBuilder = queryBuilder & " + ']'"

    buildSQL = queryBuilder

End Function

Ответы [ 2 ]

2 голосов
/ 07 ноября 2019

Причиной ошибки является Тип данных Приоритет . Вы пытаетесь объединить значение datetime в varchar, и, поскольку datetime имеет более высокий приоритет, чем 2, SQL Server пытается неявно преобразовать varchar в datetime;что, очевидно, ошибки.

Вам необходимо явно преобразовать значение datetime в varchar. Я здесь использую формат ISO yyyyMMdd style , но вам может потребоваться изменить его на другой (и соответственно увеличить размер varchar):

SELECT STUFF((SELECT ',{"account_no":"' + account_no + '"' + ',"version_num":"' + version_num + '"' + ',"user_id":"' + user_id + '"' + ',"time_stamp":"' + CONVERT(varchar(8), time_stamp, 112) + '"' + '}'
              FROM uAccountHighLevel
              WHERE account_no = '3718035'
                AND version_num = '37'
             FOR XML PATH(''), TYPE).value('.', 'varchar(max)'),1,1,'');
0 голосов
/ 07 ноября 2019

Я смог заставить его работать, заставив все до VARCHAR(Max).

Итак, петлитель столбца queryBuilder теперь читает:

queryBuilder = queryBuilder & """" & rs.Fields(0).Value & """:""' + CAST(" & rs.Fields(0).Value & " AS VARCHAR(MAX)) + '""' + ',"

, который производит это:

SELECT
    STUFF((
        SELECT',{"account_no":"' + CAST(account_no AS VARCHAR(MAX) + '"' + ',"version_num":"' + CAST(version_num AS VARCHAR(MAX) + '"' + ',"user_id":"' + CAST(user_id AS VARCHAR(MAX) + '"' + ',"time_stamp":"' + CAST(time_stamp AS VARCHAR(MAX) + '"' + '}' 
        FROM uAccountHighLevel 
        WHERE account_no='3718035' and version_num='37' 
        FOR XML path(''), type).value('.', 'varchar(max)')
        , 1, 1, '')
...