【问题标题】:COLDFUSION: How to GROUP by the first column and convert second column into three separate columnsCOLDFUSION:如何按第一列分组并将第二列转换为三个单独的列
【发布时间】:2013-06-10 22:07:32
【问题描述】:

这是我在 cfquery 输出之前的“employeeRatings”表中使用的 RAW DATA 示例:

        (showcasing employeeID:1128 for the month of May)

employeeID  |   Possible_Factor  |   Factor   |  ratingDate
=======================================================================
 1128       |        .1          |  .1        | 5/25/2013 2:05:13 PM 
 1128       |        .1          |  .0        | 5/22/2013 9:30:43 AM 
 1128       |        .2          |  .1        | 5/17/2013 9:42:09 AM 
 1128       |        .1          |  .1        | 5/13/2013 8:07:15 AM 
 1128       |        .1          |  .0        | 5/10/2013 7:52:51 AM 
 1128       |        .4          |  .0        | 5/6/2013 12:41:12 PM 

这是 cfquery(SQL 语句):

SELECT ROUND(100 * (SUM(Factor) / SUM(Possible_Factor)), 2) AS employeeRating, CONVERT(CHAR(4), ratingDate, 100) + CONVERT(CHAR(4), ratingDate, 120) AS month, employeeID, DATEADD(MONTH, DATEDIFF(MONTH, 0, ratingDate), 0) AS shortdate 
FROM employeeRatings
GROUP BY CONVERT(CHAR(4), ratingDate, 100) + CONVERT(CHAR(4), ratingDate, 120), DATEADD(MONTH, DATEDIFF(MONTH, 0, ratingDate), 0), employeeID 
ORDER BY employeeID, DATEADD(MONTH, DATEDIFF(MONTH, 0, ratingDate), 0) DESC

cfquery 之后的输出会是这样的:

employeeID  |   employeeRating   |   month      |  shortdate
=======================================================================
 1128       |        30          |  May 2013    | 5/1/2013 12:00:00 AM 
 1128       |        60          |  April 2013  | 4/1/2013 12:00:00 AM
 1128       |        90          |  Jan 2013    | 1/1/2013 12:00:00 AM
 7310000    |        95          |  April 2013  | 4/1/2013 12:00:00 AM
 7310000    |        85          |  Mar 2013    | 3/1/2013 12:00:00 AM
 7310000    |        75          |  Feb 2013    | 2/1/2013 12:00:00 AM
 7310000    |        55          |  Jan 2013    | 1/1/2013 12:00:00 AM
 444981     |        27          |  Mar 2013    | 3/1/2013 12:00:00 AM
 444981     |        77          |  Jan 2013    | 1/1/2013 12:00:00 AM
 444981     |        97          |  Nov 2012    | 11/1/2012 12:00:00 AM
 444981     |        37          |  Sept 2012   | 9/1/2012 12:00:00 AM
 444981     |        47          |  Aug 2012    | 8/1/2012 12:00:00 AM

我需要一个员工并列出他们最后三个评级(如果月份为空,则跳过空月份并获得下个月的评级,以显示最后三个记录的评级)。这是一个动态 cfquery,列出了 200 多名员工。以下是所需的输出:

supplierID  |   LastRating   |   SecondLastRating  |   ThirdLastRating
======================================================================
 1128       |        30      |       60            |         90
 7310000    |        95      |       85            |         75
 444981     |        27      |       77            |         97

我在 SQL Server 2000(兼容性 80)上使用 ColdFusion,但是我使用的 ColdFusion 版本不支持 cfloop 组属性。我想获取新的输出并将其放入一个新的查询中,以便它可以与另一个查询连接。一个解决方案 = 来自 FB 的星巴克礼物 ;) 谢谢大家的时间和考虑!!!!

【问题讨论】:

  • 您使用的是哪个 DBMS?
  • 不使用分组cfloop使用分组cfoutput,在所有版本的CF中都有效
  • 是否可以修改原始数据库查询?如果是这样,请更新问题标签以指示您的数据库类型/版本。
  • 大家好!首先,感谢大家抽出宝贵时间查看我的问题。
  • @Miguel-F,我正在使用 MS SQL Server。

标签: sql sql-server coldfusion pivot cfloop


【解决方案1】:

这是一个只有 ColdFusion 的解决方案

<table>
<tr>
    <td>SupplierID</td>
    <td>LastRating</td>
    <td>SecondLastRating</td>
    <td>ThirdLastRating</td>
</tr>
<cfoutput name="qrySupplier" group="employeeID">
<cfset Rating  = 0>
<tr>
    <td>#employeeid#</td>
    <cfoutput>
       <cfset Rating++>
       <cfif Rating LTE 3>
           <td>#employeerating#</td>
       </cfif>
     </cfoutput>
</tr>
</cfoutput>
</table>

【讨论】:

  • 这行得通!!!!!!我正在使用它!!!!太感谢了!!!这对我来说是困难的部分,但现在我会看到我可以创建一个新的查询。无论如何...我会尽力在FB上找到你并星巴克赠送你^_^再次感谢你!
  • 现在我想弄清楚的技巧是如何接受它并创建一个新查询。和建议?
  • 数据透视表会很有用
  • @Enchauva - 在这种特定情况下,模拟 2000 年的枢轴会变得有点难看/难以管理 - 除非您有能力创建视图或临时表。你?虽然不理想,但您仍然可以使用 CF 代码。请参阅我的更新答案。
【解决方案2】:

SQL Server 2005+

另一种选择是使用 SQL Server 的 PIVOT 运算符

首先使用ROW_NUMBER() 按员工和日期对记录进行排名。 (注意:如果您的表不包含实际的日期时间列,您可以替换一个标识列,或使用convert() 将“月份”转换为datetime)。

    SELECT  employeeID
            , employeeRating
            , ROW_NUMBER() OVER ( 
                    PARTITION BY employeeID 
                    ORDER BY employeeID, theRatingDateCol DESC
            ) AS Row
    FROM   yourTable
    ...

结果:

employeeID  employeeRating Row
----------- -------------- --------------------
1128        30             1
1128        60             2
1128        90             3
444981      27             1
444981      77             2
444981      97             3
444981      37             4
7310000     95             1
7310000     85             2
7310000     75             3
7310000     55             4

然后PIVOT 前三 (3) 行的结果:

    ... 
    PIVOT
    (       
            MIN(employeeRating)
            FOR Row IN ( [1],[2],[3]) 
    )

完整查询:

SELECT pvt.employeeID
        , pvt.[1] AS LastRating  
        , pvt.[2] AS SecondLastRating  
        , pvt.[3] AS ThirdLastRating  
FROM (
        --- order by employee and rating date (descending)
        SELECT  employeeID
                , employeeRating
                , ROW_NUMBER() OVER ( 
                    PARTITION BY employeeID 
                    ORDER BY employeeID, theRatingDateCol DESC
                ) AS Row
        FROM   yourTable
    ) data
    PIVOT
    (   -- take top 3 values
        MIN(employeeRating)
        FOR Row IN ( [1],[2],[3]) 
    ) pvt

结果:

employeeID  LastRating  SecondLastRating ThirdLastRating
----------- ----------- ---------------- ---------------
1128        30          60               90
444981      27          77               97
7310000     95          85               75

SQL Server 2000

很遗憾,SQL Server 2000 和更早版本不支持这些功能中的任何一个。虽然不像PIVOT 那样流畅,但您仍然可以使用子查询和CASE 来模拟它。

首先,使用子查询代替ROW_NUMBER()。本质上,您count 具有较早评级日期的记录数,并使用它代替行号。注意:这假设每个员工的评级日期是唯一的。如果不是,您将需要添加另一列来打破平局。

然后使用CASE 检查行号并为前三个记录生成列:

SELECT  r.employeeID
        , MAX( CASE WHEN r.Row = 0 THEN r.EmployeeRating ELSE 0 END ) AS LastRating  
        , MAX( CASE WHEN r.Row = 1 THEN r.EmployeeRating ELSE 0 END ) AS SecondLastRating  
        , MAX( CASE WHEN r.Row = 2 THEN r.EmployeeRating ELSE 0 END ) AS ThirdLastRating  
FROM  (
        SELECT  m.employeeID
                , m.employeeRating
                , m.theRatingDate
                , (  SELECT COUNT(*)
                     FROM   yourTable cnt
                    WHERE  cnt.employeeID = m.employeeID
                    AND    cnt.theRatingDate > m.theRatingDate
                ) AS Row
        FROM   yourTable m
        GROUP BY m.employeeID
            , m.employeeRating
            , m.theRatingDate
        ) r
WHERE  r.Row <= 2
GROUP BY r.employeeID

冷融合

最后一个选项是使用 ColdFusion。您可以调整 James Mohler's answer 以填充单独的“枢轴”查询。在查询循环之前,创建一个新的查询对象,并按顺序命名评级列,即rating1,rating2,rating3。在外部循环内,为每个员工添加一行。最后,使用计数器填充内部循环中的前三列。

注意:原始查询必须employeeID, shortDate DESC排序,否则代码将无法正常工作。

<cfset newQuery = queryNew("employeeID,rating1,rating2,rating3", "integer,Decimal,Decimal,Decimal")>

<cfoutput query="originalQuery" group="employeeID">
    <!--- add new employee row --->
    <cfset ratingRow = queryAddRow(newQuery, 1)>
    <cfset newQuery["employeeID"][ratingRow] = employeeID>

    <!--- initialize rating counter --->
    <cfset ratingIndex = 0>
    <cfoutput>
        <cfset ratingColumn++>
        <!--- populate top 3 rating columns --->
        <cfif ratingColumn lte 3>
            <cfset newQuery["rating"& ratingColumn][ratingRow] = employeeRating>
        </cfif>
    </cfoutput>
</cfoutput>

【讨论】:

  • 谢谢利!!!我试过你的编码,但是我的服务器是 SQL Server (80),它不支持“ROW_NUMBER()”函数。只有 SQL Server 2005 (90)、SQL Server 2008 (100) 或 SQL Server 2012 (110) 支持它。悲伤的一天,因为您的解决方案看起来很棒而且很有前途!!!! :(
  • 哦,那PIVOT 也出来了。两者都是在 2000 年之后推出的。但是,您可以使用子查询和 case 来模拟它。它不是那么漂亮,但我必须为遗留系统这样做。我将发布 SQL Server 2000 的更新示例。
  • Leigh...你简直太棒了!!!现在正在审查并插入您的解决方案。我会在一小时内提供状态更新! 手指交叉
  • 再次感谢李!一切正常,但是“CASE WHEN r.Row = 0 ...”语句没有捕捉到正确的评级,我明白为什么。当我们要求 inner-inner 语句进行行计数时,它是在计算员工评分摘要之前对行进行计数。原始的“员工工作分配”表具有特定员工每个月的不同工作量,cfquery 将该数据压缩为员工在特定月份的月度评级。我将在查询之前提供原始数据表的示例...这非常接近解决方案!!!
  • (编辑)从技术上讲,你可以让它工作,但考虑到新的要求现有的限制,所需的 sql 会变得有点荒谬......你真正需要什么是创建一个view 或临时表,并使用它来代替yourTable。该权限在您的环境中可用吗?如果没有,此时您最好使用 CF。在不知道为什么需要枢轴来加入另一个查询的情况下,很难说这是否是 only 选项。
【解决方案3】:

您可以尝试以下方法来帮助您入门。我离开了cmets。我没有权限建立一个快速表,所以它未经测试但可能是一个很好的开始。我考虑到您的员工/供应商的评分可能多于或少于 3 个。

<!--- Counter to count ratings  --->
<Cfset x=0>
<table width="600" border="0" cellspacing="0" cellpadding="0">
  <tr>
    <td>supplierid</td>
    <td>last rating</td>
    <td>second last rating</td>
    <td>thirdlastrating</td>
  </tr>
<!--- Group by employee --->
<cfoutput query="yourQuery" group="employeeid">
    <!--- if previous employee had less then 3 ratings, close off table --->
    <cfif x gt 0 and x lt 3>
         <cfif x eq 1><td>&nbsp</td><td>&nbsp;</td></tr></cfif>
         <cfif x eq 2><td>&nbsp;</td></tr></cfif>
    </cfif>
    <!--- Loop through employee --->
  <tr>
    <td>#employeeid#</td>
    <!--- Check counter to make sure we are only doing 3 ratings per line --->
    <cfif x lt 3>
        <cfoutput>
        <td>#employeerating#</td>
        <cfset x=x+1>
        </cfoutput>
    </cfif>
    <!--- If at the 3rd rating, close off the row --->
    <cfif x eq 3>
        </tr>
        <cfset x=0>
        <!--- if at 3rd rating, reset counter --->
    </cfif>
</cfoutput>
</table>

【讨论】:

  • 它适用于第一个员工,但没有填充其他员工的评级。我会再试一次,以三次检查我没有过度看东西。无论如何,我非常感谢您的支持和您的方法!!!! :D
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-22
  • 2021-05-01
  • 2022-11-12
  • 2022-11-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多