存储用户帐户权限的最佳方式?
Best way to store permissions for the user account?
我有与我的应用程序中的每个帐户相关联的权限记录。每个账户根据账户类型可以有一个或多个权限记录。这是示例:
<cfquery name="qryUserPerm" datasource="#Application.dsn#">
SELECT AccessType, AccessLevel, State, City, Building
FROM Permissions
WHERE AccountID = <cfqueryparam cfsqltype="cf_sql_integer" value="#trim(session.AccountID)#">
</cfquery>
上面的查询将为其中一个帐户生成这样的数据:
RecID AccountID AccessType AccessLevel State City Building
70 285A637D82B9 F B NY New York 8010
71 285A637D82B9 F B NY New York 5412
73 285A637D82B9 F B NY New York 6103
74 285A637D82B9 F B NY New York 3106
如您在上方所见,此帐户分配了 4 条记录。访问类型可以是完全 F
或仅查看 V
。访问级别可以是州 'S'、城市 'C' 或建筑物 'B'。用户当时只能分配给他们一个访问级别,因此例如没有用户可以分配城市和州级别的情况。我的问题是,针对特定访问级别组织查询数据的最佳方式是什么?在这种情况下,我必须合并列表或数组中的 4 条记录。 State 级别只能分配一个权限记录,City 和 Building 可以有多个记录。这是我所拥有的示例:
<cfset local.permissionType = "">
<cfset local.permissionLevel = "">
<cfset local.permissionList = "">
<cfloop query="qryUserPerm">
<cfif qryUserPerm.AccessLevel EQ "S">
<cfset local.permissionType = qryUserPerm.AccessType>
<cfset local.permissionLevel = qryUserPerm.AccessLevel>
<cfset local.permissionList = listAppend(permissionList, "", ",")>
<cfelseif qryUserPerm.AccessLevel EQ "C">
<cfset local.permissionType = qryUserPerm.AccessType>
<cfset local.permissionLevel = qryUserPerm.AccessLevel>
<cfset local.permissionList = listAppend(permissionList, qryUserPerm.City, ",")>
<cfelseif qryUserPerm.AccessLevel EQ "B">
<cfset local.permissionType = qryUserPerm.AccessType>
<cfset local.permissionLevel = qryUserPerm.AccessLevel>
<cfset local.permissionList = listAppend(permissionList, qryUserPerm.Building, ",")>
<cfelse>
<cfset local.permissionType = "">
<cfset local.permissionLevel = "">
<cfset local.permissionList = listAppend(permissionList, "", ",")>
</cfif>
</cfloop>
将 permissionType
和 permissionLevel
保留在循环中似乎是多余的,但我目前不知道更好的方法来避免这种情况。如果我必须比较权限列表,这也会使过程变得非常困难。我将不得不 运行 这个相同的过程并构建列表,以便在当前登录的用户更改其权限的情况下将其与 Session.premissionList
进行比较。有什么方法可以将这些记录与 SQL 合并吗?或者这种方法是更好的选择?
我很想删除循环。我在想这可能会使事情变得简单一些。
<cfset local.permissionType = "">
<cfset local.permissionLevel = "">
<cfset local.permissionList = "">
<cfif qryUserPerm.AccessLevel EQ "S">
<cfset local.permissionType = qryUserPerm.AccessType>
<cfset local.permissionLevel = qryUserPerm.AccessLevel>
<cfset local.permissionList = qryUserPerm.State>
<cfelseif qryUserPerm.AccessLevel EQ "C">
<cfset local.permissionType = qryUserPerm.AccessType>
<cfset local.permissionLevel = qryUserPerm.AccessLevel>
<cfset local.permissionList = ListRemoveDuplicates(ValueList(permissionList,qryUserPerm.City))>
<cfelseif qryUserPerm.AccessLevel EQ "B">
<cfset local.permissionType = qryUserPerm.AccessType>
<cfset local.permissionLevel = qryUserPerm.AccessLevel>
<cfset local.permissionList = ListRemoveDuplicates(ValueList(permissionList,qryUserPerm.Building))>
</cfif>
而且,如果您想在以后比较列表的相等性,您可能需要使用:
<cfset local.permissionList = ListSort(local.permissionList,"textnocase","asc")>
更新:
<cfscript>
qryUserPerm = queryExecute("
SELECT AccessType, AccessLevel, State, City, Building
FROM Permissions
WHERE AccountID = :AccountID
",
{
AccountID = {value = Trim(session.AccountID), cfsqltype = "cf_sql_integer"}
},
{
datasource = Application.dsn
});
local.permissionType = "";
local.permissionLevel = "";
local.permissionList = "";
if(qryUserPerm.AccessLevel EQ "S"){
local.permissionType = qryUserPerm.AccessType;
local.permissionLevel = qryUserPerm.AccessLevel;
local.permissionList = qryUserPerm.State;
}
else if(qryUserPerm.AccessLevel EQ "C"){
local.permissionType = qryUserPerm.AccessType;
local.permissionLevel = qryUserPerm.AccessLevel;
local.permissionList = ListRemoveDuplicates(ValueList(permissionList,qryUserPerm.City));
}
else if(qryUserPerm.AccessLevel EQ "B"){
local.permissionType = qryUserPerm.AccessType;
local.permissionLevel = qryUserPerm.AccessLevel;
local.permissionList = ListRemoveDuplicates(ValueList(permissionList,qryUserPerm.Building));
}
</cfscript>
您可以 trim 通过使用 CASE 根据访问级别将所有内容合并到一个列中来降低代码。
SELECT AccessType
, AccessLevel
, CASE AccessLevel
WHEN 'C' THEN City
WHEN 'B' THEN Building
WHEN 'S' THEN State
END AS AccessValue
FROM Permissions
WHERE AccountID = <cfqueryparam cfsqltype="cf_sql_integer" value="#session.AccountID#">
然后根据该列构建您的列表。不需要 cfif。
<cfset local.permissionType = qryUserPerm.AccessType>
<cfset local.permissionLevel = qryUserPerm.AccessLevel>
<cfset local.permissionList = valueList(qryUserPerm.AccessValue)>
您也可以构建 CSV list in SQL only,但不确定在这种情况下是否值得,因为在 CF 中构建它同样容易。
SELECT TOP 1 AccessType
, AccessLevel
, STUFF(( SELECT ','+ l.AccessValue
FROM ( SELECT CASE AccessLevel
WHEN 'C' THEN City
WHEN 'B' THEN Building
WHEN 'S' THEN State
END AS AccessValue
FROM Permissions l
WHERE l.AccountID = p.AccountID
) l
GROUP BY l.AccessValue
FOR XML PATH('')
),1,1,'') AS PermissionsList
FROM Permissions p
WHERE AccountID = <cfqueryparam cfsqltype="cf_sql_integer" value="#session.AccountID#">
无论如何,使用上面的查询将 return 您需要的所有内容都在一行中:AccessType、AccessLevel 和 csv 权限列表。
<cfset local.permissionType = qryUserPerm.AccessType>
<cfset local.permissionLevel = qryUserPerm.AccessLevel>
<cfset local.permissionList = qryUserPerm.PermissionsList>
这可以在 SQL 本身中完成,这可能比在代码中操作数据更高效。
数据的一个问题是 State
、City
和 Building
列需要逆透视然后变成逗号分隔列表。
由于您使用的是 SQL 2008,因此您可以使用所需的功能。
查询是:http://sqlfiddle.com/#!18/0f4f7/1
; WITH cte AS (
SELECT
AccountID, AccessType, AccessLevel
, CASE AccessLevel
WHEN 'S' THEN State
WHEN 'C' THEN City
WHEN 'B' THEN Building
END AS Permissions
FROM Permissions
WHERE AccountID =
<cfqueryparam cfsqltype="cf_sql_integer" value="#session.AccountID#">
/* Dynamic variable here */
)
SELECT DISTINCT AccountID, AccessType, AccessLevel
, CASE
WHEN AccessLevel = 'S' THEN Permissions
ELSE LEFT(ca.pl, COALESCE(LEN(ca.pl),0)-1)
END AS PermissionList
FROM cte
CROSS APPLY (
SELECT p.Permissions + ', '
FROM cte p
WHERE p.AccountID = cte.AccountID
AND p.AccessType = cte.AccessType
AND p.AccessLevel = cte.AccessLevel
FOR XML PATH('')
) ca (pl) ;
我从 CTE 开始,根据 AccessLevel
构建 Permissions
的 "unpivoted" 列表。如果这可以放在 SQL 视图中,您可以在此处省略 WHERE
语句并在调用视图时调用它。如果您可以将视图放入数据库,我会更喜欢它。
获得 CTE 后,我只是 select 基本列(AccountID
、AccessType
和 AccessLevel
,然后 CROSS APPLY
一个逗号-Permissions
的分隔列表。我使用 FOR XML PATH('')
来构建那个逗号分隔的列表。
如果能够将其转换为视图,那就简单了
<cfquery name="qryUserPerm" datasource="#Application.dsn#">
SELECT AccessType, AccessLevel, PermissionList
FROM myPermissionsView
WHERE AccountID = <cfqueryparam cfsqltype="cf_sql_integer" value="#trim(session.AccountID)#">
</cfquery>
如果没有,您将不得不在 cfquery
标签内尝试 运行 上面的完整查询。
这应该会给你一个像这样的数据集:
| AccessType | AccessLevel | PermissionList |
|------------|-------------|------------------------|
| F | B | 8010, 5412, 6103, 3106 |
您只需处理一个结果,无需循环。
============================================= =========================
如果您想走代码内路线,我仍然建议尝试使用 cfscript
构建结构。但是,如果您可以拥有多个 AccessLevel,您的结果可能不是您认为的那样。您必须仔细检查您的数据。
local.permissionType = q2.AccessType ;
local.permissionLevel = q2.AccessLevel ;
switch( q2.AccessLevel ) {
case "S" : local.permissionList = q2.State ;
break ;
case "C" : local.permissionList = ListRemoveDuplicates(ValueList(q2.City)) ;
break ;
case "B" : local.permissionList = ListRemoveDuplicates(ValueList(q2.Building)) ;
break ;
}
https://trycf.com/gist/e811ec86f0d5a52fd9ce703f897cb5aa/acf2016?theme=monokai
我有与我的应用程序中的每个帐户相关联的权限记录。每个账户根据账户类型可以有一个或多个权限记录。这是示例:
<cfquery name="qryUserPerm" datasource="#Application.dsn#">
SELECT AccessType, AccessLevel, State, City, Building
FROM Permissions
WHERE AccountID = <cfqueryparam cfsqltype="cf_sql_integer" value="#trim(session.AccountID)#">
</cfquery>
上面的查询将为其中一个帐户生成这样的数据:
RecID AccountID AccessType AccessLevel State City Building
70 285A637D82B9 F B NY New York 8010
71 285A637D82B9 F B NY New York 5412
73 285A637D82B9 F B NY New York 6103
74 285A637D82B9 F B NY New York 3106
如您在上方所见,此帐户分配了 4 条记录。访问类型可以是完全 F
或仅查看 V
。访问级别可以是州 'S'、城市 'C' 或建筑物 'B'。用户当时只能分配给他们一个访问级别,因此例如没有用户可以分配城市和州级别的情况。我的问题是,针对特定访问级别组织查询数据的最佳方式是什么?在这种情况下,我必须合并列表或数组中的 4 条记录。 State 级别只能分配一个权限记录,City 和 Building 可以有多个记录。这是我所拥有的示例:
<cfset local.permissionType = "">
<cfset local.permissionLevel = "">
<cfset local.permissionList = "">
<cfloop query="qryUserPerm">
<cfif qryUserPerm.AccessLevel EQ "S">
<cfset local.permissionType = qryUserPerm.AccessType>
<cfset local.permissionLevel = qryUserPerm.AccessLevel>
<cfset local.permissionList = listAppend(permissionList, "", ",")>
<cfelseif qryUserPerm.AccessLevel EQ "C">
<cfset local.permissionType = qryUserPerm.AccessType>
<cfset local.permissionLevel = qryUserPerm.AccessLevel>
<cfset local.permissionList = listAppend(permissionList, qryUserPerm.City, ",")>
<cfelseif qryUserPerm.AccessLevel EQ "B">
<cfset local.permissionType = qryUserPerm.AccessType>
<cfset local.permissionLevel = qryUserPerm.AccessLevel>
<cfset local.permissionList = listAppend(permissionList, qryUserPerm.Building, ",")>
<cfelse>
<cfset local.permissionType = "">
<cfset local.permissionLevel = "">
<cfset local.permissionList = listAppend(permissionList, "", ",")>
</cfif>
</cfloop>
将 permissionType
和 permissionLevel
保留在循环中似乎是多余的,但我目前不知道更好的方法来避免这种情况。如果我必须比较权限列表,这也会使过程变得非常困难。我将不得不 运行 这个相同的过程并构建列表,以便在当前登录的用户更改其权限的情况下将其与 Session.premissionList
进行比较。有什么方法可以将这些记录与 SQL 合并吗?或者这种方法是更好的选择?
我很想删除循环。我在想这可能会使事情变得简单一些。
<cfset local.permissionType = "">
<cfset local.permissionLevel = "">
<cfset local.permissionList = "">
<cfif qryUserPerm.AccessLevel EQ "S">
<cfset local.permissionType = qryUserPerm.AccessType>
<cfset local.permissionLevel = qryUserPerm.AccessLevel>
<cfset local.permissionList = qryUserPerm.State>
<cfelseif qryUserPerm.AccessLevel EQ "C">
<cfset local.permissionType = qryUserPerm.AccessType>
<cfset local.permissionLevel = qryUserPerm.AccessLevel>
<cfset local.permissionList = ListRemoveDuplicates(ValueList(permissionList,qryUserPerm.City))>
<cfelseif qryUserPerm.AccessLevel EQ "B">
<cfset local.permissionType = qryUserPerm.AccessType>
<cfset local.permissionLevel = qryUserPerm.AccessLevel>
<cfset local.permissionList = ListRemoveDuplicates(ValueList(permissionList,qryUserPerm.Building))>
</cfif>
而且,如果您想在以后比较列表的相等性,您可能需要使用:
<cfset local.permissionList = ListSort(local.permissionList,"textnocase","asc")>
更新:
<cfscript>
qryUserPerm = queryExecute("
SELECT AccessType, AccessLevel, State, City, Building
FROM Permissions
WHERE AccountID = :AccountID
",
{
AccountID = {value = Trim(session.AccountID), cfsqltype = "cf_sql_integer"}
},
{
datasource = Application.dsn
});
local.permissionType = "";
local.permissionLevel = "";
local.permissionList = "";
if(qryUserPerm.AccessLevel EQ "S"){
local.permissionType = qryUserPerm.AccessType;
local.permissionLevel = qryUserPerm.AccessLevel;
local.permissionList = qryUserPerm.State;
}
else if(qryUserPerm.AccessLevel EQ "C"){
local.permissionType = qryUserPerm.AccessType;
local.permissionLevel = qryUserPerm.AccessLevel;
local.permissionList = ListRemoveDuplicates(ValueList(permissionList,qryUserPerm.City));
}
else if(qryUserPerm.AccessLevel EQ "B"){
local.permissionType = qryUserPerm.AccessType;
local.permissionLevel = qryUserPerm.AccessLevel;
local.permissionList = ListRemoveDuplicates(ValueList(permissionList,qryUserPerm.Building));
}
</cfscript>
您可以 trim 通过使用 CASE 根据访问级别将所有内容合并到一个列中来降低代码。
SELECT AccessType
, AccessLevel
, CASE AccessLevel
WHEN 'C' THEN City
WHEN 'B' THEN Building
WHEN 'S' THEN State
END AS AccessValue
FROM Permissions
WHERE AccountID = <cfqueryparam cfsqltype="cf_sql_integer" value="#session.AccountID#">
然后根据该列构建您的列表。不需要 cfif。
<cfset local.permissionType = qryUserPerm.AccessType>
<cfset local.permissionLevel = qryUserPerm.AccessLevel>
<cfset local.permissionList = valueList(qryUserPerm.AccessValue)>
您也可以构建 CSV list in SQL only,但不确定在这种情况下是否值得,因为在 CF 中构建它同样容易。
SELECT TOP 1 AccessType
, AccessLevel
, STUFF(( SELECT ','+ l.AccessValue
FROM ( SELECT CASE AccessLevel
WHEN 'C' THEN City
WHEN 'B' THEN Building
WHEN 'S' THEN State
END AS AccessValue
FROM Permissions l
WHERE l.AccountID = p.AccountID
) l
GROUP BY l.AccessValue
FOR XML PATH('')
),1,1,'') AS PermissionsList
FROM Permissions p
WHERE AccountID = <cfqueryparam cfsqltype="cf_sql_integer" value="#session.AccountID#">
无论如何,使用上面的查询将 return 您需要的所有内容都在一行中:AccessType、AccessLevel 和 csv 权限列表。
<cfset local.permissionType = qryUserPerm.AccessType>
<cfset local.permissionLevel = qryUserPerm.AccessLevel>
<cfset local.permissionList = qryUserPerm.PermissionsList>
这可以在 SQL 本身中完成,这可能比在代码中操作数据更高效。
数据的一个问题是 State
、City
和 Building
列需要逆透视然后变成逗号分隔列表。
由于您使用的是 SQL 2008,因此您可以使用所需的功能。
查询是:http://sqlfiddle.com/#!18/0f4f7/1
; WITH cte AS (
SELECT
AccountID, AccessType, AccessLevel
, CASE AccessLevel
WHEN 'S' THEN State
WHEN 'C' THEN City
WHEN 'B' THEN Building
END AS Permissions
FROM Permissions
WHERE AccountID =
<cfqueryparam cfsqltype="cf_sql_integer" value="#session.AccountID#">
/* Dynamic variable here */
)
SELECT DISTINCT AccountID, AccessType, AccessLevel
, CASE
WHEN AccessLevel = 'S' THEN Permissions
ELSE LEFT(ca.pl, COALESCE(LEN(ca.pl),0)-1)
END AS PermissionList
FROM cte
CROSS APPLY (
SELECT p.Permissions + ', '
FROM cte p
WHERE p.AccountID = cte.AccountID
AND p.AccessType = cte.AccessType
AND p.AccessLevel = cte.AccessLevel
FOR XML PATH('')
) ca (pl) ;
我从 CTE 开始,根据 AccessLevel
构建 Permissions
的 "unpivoted" 列表。如果这可以放在 SQL 视图中,您可以在此处省略 WHERE
语句并在调用视图时调用它。如果您可以将视图放入数据库,我会更喜欢它。
获得 CTE 后,我只是 select 基本列(AccountID
、AccessType
和 AccessLevel
,然后 CROSS APPLY
一个逗号-Permissions
的分隔列表。我使用 FOR XML PATH('')
来构建那个逗号分隔的列表。
如果能够将其转换为视图,那就简单了
<cfquery name="qryUserPerm" datasource="#Application.dsn#">
SELECT AccessType, AccessLevel, PermissionList
FROM myPermissionsView
WHERE AccountID = <cfqueryparam cfsqltype="cf_sql_integer" value="#trim(session.AccountID)#">
</cfquery>
如果没有,您将不得不在 cfquery
标签内尝试 运行 上面的完整查询。
这应该会给你一个像这样的数据集:
| AccessType | AccessLevel | PermissionList |
|------------|-------------|------------------------|
| F | B | 8010, 5412, 6103, 3106 |
您只需处理一个结果,无需循环。
============================================= =========================
如果您想走代码内路线,我仍然建议尝试使用 cfscript
构建结构。但是,如果您可以拥有多个 AccessLevel,您的结果可能不是您认为的那样。您必须仔细检查您的数据。
local.permissionType = q2.AccessType ;
local.permissionLevel = q2.AccessLevel ;
switch( q2.AccessLevel ) {
case "S" : local.permissionList = q2.State ;
break ;
case "C" : local.permissionList = ListRemoveDuplicates(ValueList(q2.City)) ;
break ;
case "B" : local.permissionList = ListRemoveDuplicates(ValueList(q2.Building)) ;
break ;
}
https://trycf.com/gist/e811ec86f0d5a52fd9ce703f897cb5aa/acf2016?theme=monokai