SQL Server显示具有相同值的第一个,第二个和第三个值

时间:2015-12-18 17:21:49

标签: sql-server sql-server-2012

我正在尝试在SQL Server中创建一个查询但遇到一些困难。我将尝试提供一些示例数据,以便更容易。我试图从中获取多个表:

单位:

UnitID
------
 101
 102
 103
 104
 etc..

工作:

JobID 
------
 1    
 2  
etc.

工作单位:

UnitID | JobID | DispatchDate
-----------------------------
 102   |  12   |  Dec 12 2015
 104   |  14   |  Dec 12 2015
 102   |  18   |  Dec 12 2015
 108   |  18   |  Dec 12 2015
 102   |  11   |  Dec 12 2015
 104   |  10   |  Dec 12 2015

我想要的结果会反映出这个数据集:

UnitID | Job 1 | Job 2 | Job 3 
------------------------------
  102  |  12   |  18   |  11
  103  |       |       |
  104  |  14   |  10   |
  105  |       |       |
  106  |       |       |
  107  |       |       |
  108  |  18   |       |

所以基本上,我想展示单位最多可以完成三个工作的工作,但是我仍然需要展示其他没有出去的单位,或者只是出去一次或两次。

我目前正在将此数据集导出到三个单独的列表视图并使用三个单独的存储过程,但这不是完成工作并且是一团糟,所以我甚至不打算发布我的代码,但如果需要,我可以。

非常感谢任何帮助。谢谢!

编辑:根据/ u / Pasty的请求,这是我正在处理的代码:

select UnitID, case when (select COUNT(JobID)
                                                                from JobUnits
                                                                where UnitID=U.UnitID
                                                                and DispatchDate='Dec 12 2015') >= 1
                                                        then (select top 1 JobID
                                                                from JobUnits
                                                                where UnitID=U.UnitID
                                                                and DispatchDate='Dec 12 2015'
                                                                order by JobID asc )
                                                        else null 
                                                        end as 'Job 1', case when (select COUNT(JobID)
                                                                from JobUnits
                                                                where UnitID=U.UnitID
                                                                and DispatchDate='Dec 12 2015') >= 2
                                                        then (select top 1 JobID
                                                                from JobUnits
                                                                where UnitID=U.UnitID
                                                                and DispatchDate='Dec 12 2015'
                                                                order by JobID desc )
                                                        else null
                                                        end as 'Job 2', case when (select COUNT(JobID)
                                                                from JobUnits
                                                                where UnitID=U.UnitID
                                                                and DispatchDate='Dec 12 2015') >= 3
                                                        then (select top 1 JobID
                                                                from JobUnits
                                                                where UnitID=U.UnitID
                                                                and DispatchDate='Dec 12 2015'
                                                                order by JobID asc )
                                                        else null
                                                        end as 'Job 3'
from Units U
order by UnitID asc

4 个答案:

答案 0 :(得分:1)

您需要使用LEFT JOIN才能获得所需的结果。如果您有 A B 表,并希望在B 中显示匹配项以及A中的所有项目, LEFT JOIN 允许你这样做。 SQL JOINS can be found here - A Visual Explanation of SQL Joins的良好视觉解释。

如何在您的案例中创建所需的输出(我假设表名为Job,Unit ans Unit_has_Job):

Unit | UnitId
-----------
        101
        102
        103
        104
        105
        107

Job | JobId
--------------
        1
        2
        3
        4
        5

Unit_has_JobId | UnitId | JobId
----------------------------------
                101  | 1
                101  | 3
                101  | 4
                102  | 4
                105  | 3

select u.UnitId, ISNULL(j.JobId, 0) as [JobId]  from unit as u
left join unit_has_job as uhj on u.UnitId = uhj.UnitId
left join job as j on uhj.JobId = j.JobId

结果是:

UnitId | JobId
101 | 1
101 | 3
101 | 4
102 | 4
103 | 0
104 | 0
105 | 3
107 | 0

我在SQL-Fiddle创建了一个示例。

使用ISNULL function标记插槽。每个JobId的不同UnitId等同于所需输出中的列。

使用LINQ2SQLGroupBy method,您可以轻松地对结果中的每个单位的作业进行分组,然后对其进行迭代:

var jobsPerUnit = result.GroupBy (r => r.UnitId);
foreach (var jobs in jobsPerUnit)
{
    Console.WriteLine("Unit: " + jobs.Key);
    foreach (var job in jobs)
    {
        if (job.JobId > 0)
        {
            Console.WriteLine("Job: " + job.JobId);
        }
    }
    Console.WriteLine("=================");
}

输出:

Unit: 101
Job: 1
Job: 3
Job: 4
=================
Unit: 102
Job: 4
=================
Unit: 103
=================
Unit: 104
=================
Unit: 105
Job: 3
=================
Unit: 107
=================

SQL中的列是选择/投影的结果,无法动态创建。一种可能的解决方案是使用dynamic SQL来创建临时表,填充它然后从这个临时表中选择,但开销可能不值得。你仍然需要一个光标来解决这个问题。

如果你想在SQL端做所有事情,那么一个可能的解决方案就是用UnitId上的结果和分组迭代光标。

答案 1 :(得分:1)

有一种更简单的方法可以获得相同的结果,您不需要一直运行内部选择。如果你使用正确的索引,这会产生更简单的执行计划和更快的执行。

请注意,内部查询(LEFT JOIN JobUnits ju)中存在LEFT JOIN,这将为Units中的每一行生成一行,无论它是否具有相关的JobUnit。

select UnitID, 
    SUM(CASE WHEN cnt=1 THEN JobID ELSE 0 END) AS Job1,
    SUM(CASE WHEN cnt=2 THEN JobID ELSE 0 END) AS Job2,
    SUM(CASE WHEN cnt=3 THEN JobID ELSE 0 END) AS Job3,
    SUM(CASE WHEN cnt=4 THEN JobID ELSE 0 END) AS Job4,
    SUM(CASE WHEN cnt=5 THEN JobID ELSE 0 END) AS Job5
FROM (
    select ju.UnitID, ju.JobID, count(1) as cnt
    FROM Units u
    LEFT JOIN JobUnits ju on (u.UnitID = ju.UnitID)
    LEFT JOIN JobUnits ju2 on (ju.UnitID = ju2.UnitID AND ju.UnitID <= ju2.UnitID)
)
GROUP BY UnitID

这个SELECT的唯一缺点就是它不会保留订单(如果是102则是12,18,11),而是按升序排序(11,12,18) - 我不知道是否在您的商业案例中,这是一个优势或劣势。

如果您比较ROWNUM s,您可以保留订单,但这需要两个以下的子选择,而且我不确定它是否值得。

答案 2 :(得分:1)

我只是在玩耍,这就是我想出来的:

SQLFiddle

IF OBJECT_ID(N'dbo.UNITS', 'U') IS NOT NULL
    DROP TABLE dbo.UNITS
GO

CREATE TABLE UNITS(ID INT IDENTITY(1, 1), UnitID INT, JobID INT, DispatchDate DATETIME)
GO

INSERT INTO UNITS 
    VALUES(102, 12, 'Dec 12 2015')
           ,(104, 14, 'Dec 12 2015')
           ,(102, 18, 'Dec 12 2015')
           ,(108, 18, 'Dec 12 2015')
           ,(102, 11, 'Dec 12 2015')
           ,(104, 10, 'Dec 12 2015')
           ,(103, NULL, NULL)
           ,(105, NULL, NULL)
           ,(106, NULL, NULL)
           ,(107, NULL, NULL)
GO

SELECT
    UnitId
    ,Job_1 = JobIDs.value('/JobID[1]','INT')
    ,Job_2 = JobIDs.value('/JobID[2]','INT')
    ,Job_3 = JobIDs.value('/JobID[3]','INT')
FROM
    (
       SELECT
          UnitID
          ,JobIDs = CONVERT(XML,'<JobID>' 
                          + REPLACE(Units.JobIDs, '|', '</JobID><JobID>') 
                          + '</JobID>')
       FROM
          (
             SELECT DISTINCT
                U.UnitId
                ,JobIDs = STUFF((
                          SELECT
                             '|' + CAST(UU.JobID AS NVARCHAR(25))
                          FROM
                             UNITS UU
                          WHERE
                             UU.UnitID = U.UnitID
                          ORDER BY
                             UU.ID, UU.JobID
                    FOR XML PATH (''), type).value('.', 'nvarchar(max)'), 1, 1, '')
             FROM
                UNITS U
             GROUP BY
                U.UnitID) Units) UJ

enter image description here

答案 3 :(得分:0)

我最终用相当多的代码解决了这个问题,但它确实有效,而且我很累,所以我只是想用它。

select UnitID, case when (select COUNT(JobID)
                                                                from JobUnits
                                                                where UnitID=U.UnitID
                                                                and DispatchDate='Dec 4 2015') >= 1
                                                        then (select top 1 JobID
                                                                from JobUnits
                                                                where UnitID=U.UnitID
                                                                and DispatchDate='Dec 4 2015'
                                                                order by JobID asc )
                                                        else null 
                                                        end as 'Job 1', case when (select COUNT(JobID)
                                                                                    from JobUnits
                                                                                    where UnitID=U.UnitID
                                                                                    and DispatchDate='Dec 4 2015') >= 1
                                                                        then (select Customer
                                                                                from Customers C
                                                                                full
                                                                                where CustomerID=())
                                                                        else null 
                                                                        end as 'Customer 1', 
                                                        case when (select COUNT(JobID)
                                                                from JobUnits
                                                                where UnitID=U.UnitID
                                                                and DispatchDate='Dec 4 2015') >= 2
                                                        then 
                                                        (SELECT TOP 1 JobID 
                                                        FROM 
                                                        ( 
                                                            SELECT TOP 2 JobID 
                                                            from JobUnits
                                                            where UnitID=U.UnitID
                                                            and DispatchDate='Dec 4 2015'
                                                            ORDER BY JobID desc
                                                        ) sub 
                                                        ORDER BY JobID asc) 
                                                        else null
                                                        end as 'Job 2', 
                                                        case when (select COUNT(JobID)
                                                                from JobUnits
                                                                where UnitID=U.UnitID
                                                                and DispatchDate='Dec 4 2015') >= 3
                                                        then 
                                                        (SELECT TOP 1 JobID 
                                                        FROM 
                                                        ( 
                                                            SELECT TOP 3 JobID 
                                                            from JobUnits
                                                            where UnitID=U.UnitID
                                                            and DispatchDate='Dec 4 2015'
                                                            ORDER BY JobID desc
                                                        ) sub 
                                                        ORDER BY JobID desc) 
                                                        else null
                                                        end as 'Job 3'
from Units U
order by UnitID asc