如何获取关联数组符号以分配简单值
How to get associative arrays notation to assign simple values
我使用 Microsoft SQL Server Northwind 演示数据库创建了示例代码。如果您无权访问此演示数据库,这里有一个简单的 (MS-SQL) 脚本,用于为该问题创建 table 和一行数据。
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 "variables.useTempVar = false", the next line will throw a "Complex object types cannot be converted to simple values. " error.<br>
<cfset variables['aPhrase'] = "I ordered " & variables['stProduct']['ProductName'] & " for " & DollarFormat(variables['stProduct']['UnitPrice']) & ".">
<cfoutput>#variables['aPhrase']#<br></cfoutput>
上面的代码在顶部有一个名为“variables.useTempVar”的布尔变量,可以将其翻转以查看我遇到的错误。
看起来从查询到结构的直接赋值(当 variables.useTempVar = false 时)导致结构值是 JVM 类型“coldfusion.sql.QueryColumn”。
另注:如果这行代码:
<cfset variables['stProduct'][vcColName] = variables.temp>
改为:
<cfset variables['stProduct'][vcColName] = variables['temp']>
JVM 数据类型将为“coldfusion.sql.QueryColumn”。
当使用点符号临时变量分配查询字段时(当variables.useTempVar = true); JVM 数据类型是与数据库列类型(java.lang.Integer、java.math.BigDecimal、java.lang.String 等)非常匹配的简单类型。
我也尝试过这样的陈述,结果有些奇怪:
<cfset variables['stProduct'][vcColName] = qryNWProducts[vcColName].toString()>
这是问题。这是将简单值从查询传输到结构的最佳方式吗?被迫使用临时变量和点符号来完成这项工作似乎很奇怪。
评论:我一直认为点符号和关联数组符号是等价的。此代码示例似乎与该观点相矛盾。
@Leigh 是正确的,因为在对查询对象使用关联数组表示法时需要提供行号。所以你会像这样引用第 1 行:qryNWProducts[vcColName][1]
关于你的问题
Is this the best way to transfer the simple values from a query to a structure?
你确定你需要一个结构吗?您的问题并没有真正指定用例,因此您完全有可能最好按原样使用查询对象。
如果您确实需要它成为结构(并且由于您使用的是 ColdFusion 11),我建议您看一下 serializeJSON/deSerializeJSON
以将其转换为结构。 serializeJSON
有一个新属性,可以将查询对象正确序列化为 "AJAX friendly" JSON 结构数组。然后,您可以将 JSON 反序列化为 CF 数组,如下所示:
NWProducts = deSerializeJSON( serializeJSON( qryNWProducts, 'struct' ) )[1];
return 该查询对象中第一行的结构表示。
虽然从 the Adobe docs for serializeJSON 看并不明显,但第二个参数可以是以下之一:true|false|struct|row|column
这将改变结果数据的格式。
这里 runnable example 使用上述技术展示每个 serializeQueryAs
选项。
开始将此类代码移至 cfscript
也是一种更好的做法。 queryExecute
非常易于使用,并且使基于脚本的查询非常容易开发。有关如何开发基于脚本的查询的更多信息,请参阅 trycf.com 上的 How To Create a Query in cfscript 教程。
最后说明,这有点偏离主题,但在命名变量时不使用 Hungarian Notation 是公认的最佳做法。
@Abram 涵盖了主要答案,但只是为了找到你提出的一个切点。
点符号和关联数组符号 在 CFML 中通常是等价的。但是,在查询的情况下,会略有不同。点符号:query.columnName
被视为 shorthand for query.columnName[currentRow]
(其中 currentRow
默认为 1
)。
带有查询的关联数组表示法没有此 "syntactic sugar",因此 query["columnName"]
指的是整个列,正如语法实际指示的那样。
据我所知,在 CFML 中没有将查询列作为参数的函数,但是如果在数组函数中使用该列,CFML 引擎会将其转换为数组。这有时非常方便。
我使用 Microsoft SQL Server Northwind 演示数据库创建了示例代码。如果您无权访问此演示数据库,这里有一个简单的 (MS-SQL) 脚本,用于为该问题创建 table 和一行数据。
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 "variables.useTempVar = false", the next line will throw a "Complex object types cannot be converted to simple values. " error.<br>
<cfset variables['aPhrase'] = "I ordered " & variables['stProduct']['ProductName'] & " for " & DollarFormat(variables['stProduct']['UnitPrice']) & ".">
<cfoutput>#variables['aPhrase']#<br></cfoutput>
上面的代码在顶部有一个名为“variables.useTempVar”的布尔变量,可以将其翻转以查看我遇到的错误。
看起来从查询到结构的直接赋值(当 variables.useTempVar = false 时)导致结构值是 JVM 类型“coldfusion.sql.QueryColumn”。
另注:如果这行代码:
<cfset variables['stProduct'][vcColName] = variables.temp>
改为:
<cfset variables['stProduct'][vcColName] = variables['temp']>
JVM 数据类型将为“coldfusion.sql.QueryColumn”。
当使用点符号临时变量分配查询字段时(当variables.useTempVar = true); JVM 数据类型是与数据库列类型(java.lang.Integer、java.math.BigDecimal、java.lang.String 等)非常匹配的简单类型。
我也尝试过这样的陈述,结果有些奇怪:
<cfset variables['stProduct'][vcColName] = qryNWProducts[vcColName].toString()>
这是问题。这是将简单值从查询传输到结构的最佳方式吗?被迫使用临时变量和点符号来完成这项工作似乎很奇怪。
评论:我一直认为点符号和关联数组符号是等价的。此代码示例似乎与该观点相矛盾。
@Leigh 是正确的,因为在对查询对象使用关联数组表示法时需要提供行号。所以你会像这样引用第 1 行:qryNWProducts[vcColName][1]
关于你的问题
Is this the best way to transfer the simple values from a query to a structure?
你确定你需要一个结构吗?您的问题并没有真正指定用例,因此您完全有可能最好按原样使用查询对象。
如果您确实需要它成为结构(并且由于您使用的是 ColdFusion 11),我建议您看一下 serializeJSON/deSerializeJSON
以将其转换为结构。 serializeJSON
有一个新属性,可以将查询对象正确序列化为 "AJAX friendly" JSON 结构数组。然后,您可以将 JSON 反序列化为 CF 数组,如下所示:
NWProducts = deSerializeJSON( serializeJSON( qryNWProducts, 'struct' ) )[1];
return 该查询对象中第一行的结构表示。
虽然从 the Adobe docs for serializeJSON 看并不明显,但第二个参数可以是以下之一:true|false|struct|row|column
这将改变结果数据的格式。
这里 runnable example 使用上述技术展示每个 serializeQueryAs
选项。
开始将此类代码移至 cfscript
也是一种更好的做法。 queryExecute
非常易于使用,并且使基于脚本的查询非常容易开发。有关如何开发基于脚本的查询的更多信息,请参阅 trycf.com 上的 How To Create a Query in cfscript 教程。
最后说明,这有点偏离主题,但在命名变量时不使用 Hungarian Notation 是公认的最佳做法。
@Abram 涵盖了主要答案,但只是为了找到你提出的一个切点。
点符号和关联数组符号 在 CFML 中通常是等价的。但是,在查询的情况下,会略有不同。点符号:query.columnName
被视为 shorthand for query.columnName[currentRow]
(其中 currentRow
默认为 1
)。
带有查询的关联数组表示法没有此 "syntactic sugar",因此 query["columnName"]
指的是整个列,正如语法实际指示的那样。
据我所知,在 CFML 中没有将查询列作为参数的函数,但是如果在数组函数中使用该列,CFML 引擎会将其转换为数组。这有时非常方便。