Как получить нотацию ассоциативных массивов для присвоения простых значений

Я создал пример кода, используя демонстрационную базу данных Microsoft SQL Server Northwind. Если у вас нет доступа к этой демонстрационной базе данных, вот простой (MS-SQL) скрипт для создания таблицы и строки данных для этого вопроса.

CREATE TABLE [dbo].[Products](
    [ProductID] [int] IDENTITY(1,1) NOT NULL,
    [ProductName] [nvarchar](40) NOT NULL,
    [SupplierID] [int] NULL,
    [CategoryID] [int] NULL,
    [QuantityPerUnit] [nvarchar](20) NULL,
    [UnitPrice] [money] NULL CONSTRAINT [DF_Products_UnitPrice]  DEFAULT (0),
    [UnitsInStock] [smallint] NULL CONSTRAINT [DF_Products_UnitsInStock]  DEFAULT (0),
    [UnitsOnOrder] [smallint] NULL CONSTRAINT [DF_Products_UnitsOnOrder]  DEFAULT (0),
    [ReorderLevel] [smallint] NULL CONSTRAINT [DF_Products_ReorderLevel]  DEFAULT (0),
    [Discontinued] [bit] NOT NULL CONSTRAINT [DF_Products_Discontinued]  DEFAULT (0),
 CONSTRAINT [PK_Products] PRIMARY KEY CLUSTERED 
(
    [ProductID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]
GO
SET IDENTITY_INSERT [dbo].[Products] ON 
GO
INSERT [dbo].[Products] ([ProductID], [ProductName], [SupplierID], [CategoryID], [QuantityPerUnit], [UnitPrice], [UnitsInStock], [UnitsOnOrder], [ReorderLevel], [Discontinued]) VALUES (1, N'Chai', 1, 1, N'10 boxes x 20 bags', 18.0000, 39, 0, 10, 0)
GO
SET IDENTITY_INSERT [dbo].[Products] OFF
GO

Вот код ColdFusion:

<cfset variables.useTempVar = false>

<cfquery datasource="Northwind2014" name="qryNWProducts">
SELECT TOP 1 * from Products;
</cfquery>

<cfdump var="#qryNWProducts#" label="qryNWProducts">

<cfset variables['stProduct'] = {}>
<cfloop index="vcColName" list="#qryNWProducts.columnlist#">
    <cfif variables.useTempVar>
        <cfset variables['temp'] = qryNWProducts[vcColName]>
        <cfset variables['stProduct'][vcColName] = variables.temp>
    <cfelse>
        <cfset variables['stProduct'][vcColName] = qryNWProducts[vcColName]>
    </cfif>
</cfloop>

<cfdump var="#variables['stProduct']#" label="variables['stProduct']">

<cfloop collection="#variables['stProduct']#" item="key"><cfoutput>
    variables['stProduct']['#key#'] JVM datatype = #getMetadata(variables['stProduct'][key]).getName()#<br>
</cfoutput></cfloop>

<br>
This always works:<br>
<cfset variables['aPhrase'] = "I ordered " &  variables.stProduct.ProductName & " for " & DollarFormat(variables.stProduct.UnitPrice) & ".">
<cfoutput>#variables['aPhrase']#<br></cfoutput>

<br>
With &quot;variables.useTempVar = false&quot;, the next line will throw a &quot;Complex object types cannot be converted to simple values. &quot; error.<br>
<cfset variables['aPhrase'] = "I ordered " &  variables['stProduct']['ProductName'] & " for " & DollarFormat(variables['stProduct']['UnitPrice']) & ".">
<cfoutput>#variables['aPhrase']#<br></cfoutput>

В приведенном выше коде есть логическая переменная с именем variable.useTempVar вверху, которую можно перевернуть, чтобы увидеть ошибку, которую я получаю.

Похоже, что прямое присвоение (когда переменные.useTempVar = false) из запроса к структуре приводит к тому, что значения структуры имеют тип JVM coldfusion.sql.QueryColumn.

Еще одно примечание: если эта строка кода:

<cfset variables['stProduct'][vcColName] = variables.temp>

изменяется на:

<cfset variables['stProduct'][vcColName] = variables['temp']>

Тип данных JVM будет coldfusion.sql.QueryColumn.

Когда временная переменная с точечной нотацией используется для назначения поля запроса (когда variable.useTempVar = true); типы данных JVM — это простые типы, которые довольно хорошо совпадают с типами столбцов базы данных (java.lang.Integer, java.math.BigDecimal, java.lang.String и т. д.).

Я также экспериментировал с такими утверждениями, и это дало некоторые странные результаты:

<cfset variables['stProduct'][vcColName] = qryNWProducts[vcColName].toString()>

Вот вопрос. Это лучший способ передать простые значения из запроса в структуру? Кажется странным, что для этой работы приходится использовать временную переменную и запись через точку.

Комментарий: я всегда думал, что точечная запись и запись ассоциативного массива эквивалентны. Этот пример кода, кажется, противоречит этому мнению.


person Scott Jibben    schedule 17.06.2015    source источник
comment
(Редактировать) Я иду спать, и у меня нет времени читать весь пост, но я скажу, что вам, кажется, не хватает номеров строк, т.е. qryNWProducts[vcColName]. При использовании нотации ассоциативного массива с объектами запроса необходимо указать номер строки запроса. В противном случае вы захватываете не тот объект: объект столбца, а не отдельное значение внутри этого столбца. Вместо этого используйте qryNWProducts[vcColName][1], где 1 — любой допустимый номер строки в запросе. Предполагая, что вам действительно нужна структура, прежде чем изобретать велосипед, проверили ли вы сайт cflib.org? cflib.org/udf/QueryRowToStruct   -  person Leigh    schedule 17.06.2015
comment
(Я понимаю, что есть также вопрос о том, почему два, казалось бы, похожих утверждения оцениваются по-разному, но я позволю кому-то менее сонному разобраться с этим ;-)   -  person Leigh    schedule 17.06.2015
comment
Спасибо @Leigh. Я должен был это знать...   -  person Scott Jibben    schedule 17.06.2015


Ответы (2)


@Leigh прав в том, что вам нужно указать номер строки при использовании нотации ассоциативного массива с объектом запроса. Таким образом, вы бы ссылались на строку 1, например: qryNWProducts[vcColName][1]

Что касается вашего вопроса

Это лучший способ передать простые значения из запроса в структуру?

Вы уверены, что вам нужна структура? В вашем вопросе на самом деле не указан вариант использования, поэтому вполне возможно, что вам лучше использовать объект запроса как есть.

Если вам нужно, чтобы это была структура (и поскольку вы используете ColdFusion 11), могу ли я предложить вам взглянуть на serializeJSON/deSerializeJSON, чтобы преобразовать это в структуру. У serializeJSON есть новый атрибут, который будет правильно сериализовать объект запроса в «дружественный к AJAX» массив структур JSON. Затем вы можете десериализовать JSON в массив CF, например:

NWProducts = deSerializeJSON( serializeJSON( qryNWProducts, 'struct' ) )[1]; Что вернет структурное представление первой строки в этом объекте запроса.

Хотя это не очевидно из документов Adobe для serializeJSON, второй параметр может быть одним of: true|false|struct|row|column который изменит формат результирующих данных.

Вот выполняемый пример использования описанной выше техники, демонстрирующий каждый serializeQueryAs вариант .

Также рекомендуется начать перемещать такой код в cfscript. queryExecute довольно прост в использовании и упрощает разработку запросов на основе скриптов. См. Как создать запрос в cfscript. учебник на сайте trycf.com, чтобы узнать больше о том, как разрабатывать запросы на основе скриптов.

Последнее замечание, и это немного не по теме, но общепринятой практикой является не использовать венгерскую нотацию при именовании переменных.

person Abram    schedule 17.06.2015
comment
Я часто использую атрибут argumentsCollection для функций. Объекты запроса не работали с argumentsCollection в прошлом. - person Scott Jibben; 17.06.2015
comment
Я, вероятно, продолжу использовать язык, основанный на тегах CF, потому что он всегда работает. У меня были проблемы с cfscript; stackoverflow .com/questions/26254498/. Adobe еще не исправила это. - person Scott Jibben; 18.06.2015
comment
Похоже, это исправлено в обновлении 5, хотя на самом деле это не веский аргумент в пользу того, чтобы не использовать cfscript, imo. Многие годами разрабатывали чисто cfscript, и хотя он не всегда был идеальным, в наши дни он определенно лучше, чем теги (особенно acf11). - person Abram; 18.06.2015
comment
Прочитайте мое обновление к этому сообщению. Я не думаю, что это полностью исправлено. - person Scott Jibben; 18.06.2015
comment
Интересно, что у Adobe есть плохая привычка действительно не исправлять ошибки. Тем не менее, я бы не стал использовать cfscript вместо отладочных меток. Если это так уж важно, обратите внимание на Lucee. - person Abram; 18.06.2015
comment
Все запросы в выводе отладки по-прежнему имеют одно и то же имя. Когда у вас есть более 50 000 строк устаревшего кода, полностью основанного на тегах, было бы серьезной задачей переделать все это в скрипт только для того, чтобы извлечь выгоду? Смешанный код скрипта + тега будет выглядеть еще хуже, чем просто код на основе тега imo. Со временем я, вероятно, вернусь на платформу Microsoft, используя C# .NET. Работаю с CF с версии 1.62. До этого были MS C++ и MFC. - person Scott Jibben; 19.06.2015
comment
Да, имея дело с несколькими унаследованными приложениями-монстрами, я полностью согласен с тем, что нет смысла переписывать такое приложение. Я подозреваю, что Абрам говорит о новом коде. На основе сценариев я определенно предпочитаю новые приложения. Он более естественен и похож на другие языки, такие как java и C#. Он прошел долгий путь, но… да, у него все еще есть некоторые досадные недостатки. - person Leigh; 20.06.2015

@ Абрам рассказал об основном ответе, но только для того, чтобы выделить одну касательную точку, которую вы поднимаете.

Точечное обозначение и обозначение ассоциативного массива в общем случае эквивалентны в CFML. Однако в случае запросов есть небольшая вариация. Точечная нотация: query.columnName рассматривается как сокращение для query.columnName[currentRow] (где currentRow по умолчанию равно 1).

Нотация ассоциативного массива с запросами не имеет этого «синтаксического сахара», поэтому query["columnName"] относится ко всему столбцу, как на самом деле указывает синтаксис.

В CFML нет известных мне функций, которые принимают столбец запроса в качестве аргумента, однако механизм CFML преобразует столбец в массив, если он используется в функции массива. Иногда это очень удобно.

person Adam Cameron    schedule 17.06.2015