COLDFUSION:如何按第一列分组并将第二列转换为三个单独的列

时间:2013-06-05 15:02:58

标签: sql sql-server coldfusion pivot cfloop

这是我在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上使用ColdFusion(兼容性80),但我使用的ColdFusion版本不支持cfloop组属性。我想采用新的输出并将其放入一个新的查询中,因此它可以与另一个查询联接。解决方案=来自FB的星巴克礼物;)谢谢大家的时间和考虑!!!!

3 个答案:

答案 0 :(得分:3)

SQL Server 2005 +

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

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

    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

最后一个选项是使用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>

答案 1 :(得分:2)

以下是仅限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>

答案 2 :(得分:1)

以下是您可以尝试让您入门的内容。我留下了评论。我没有办法建立一个快速表,所以它未经测试但可能是一个很好的首发。我考虑到您的员工/供应商可能有多于或少于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>