SQL聚合查询 - 计算空值

时间:2017-02-20 16:54:35

标签: sql tsql join count aggregate-functions

我有一个数据库设置,我们有一个工作列表。每项工作都在1到多个地点。用户可以在任何位置申请工作。他们可以分别申请到不同地点的同一工作。每个作业应用程序在进行过程中都有4种状态,从1.新到4.已关闭。

我需要运行一个SQL查询来显示每个作业的摘要,每个位置以及相关应用程序的每个状态。例如,

+ ---- + -------- + ------ + ----- +
| Job  | Location | Status | Count |
+ ---- + -------- + ------ + ----- +
| 1000 | 1        | 1      | 7     |
| 1000 | 1        | 2      | 0     |
| 1000 | 1        | 3      | 1     |
| 1000 | 1        | 4      | 1     |
| 1000 | 2        | 1      | 4     |
| 1000 | 2        | 2      | 2     |
| 1000 | 2        | 3      | 0     |
| 1000 | 2        | 4      | 8     |
+ ---- + -------- + ------ + ----- +

此外,我理想地将4个状态计数加在一起列为同一列表中的总和。

这是我到目前为止写的SQL语句: -

SELECT B.JobID, B.LocationID, B.ApplicationStatusID, COUNT(B.JobID) AS CountOfResults
FROM Job AS A
JOIN JobApplication AS B ON A.JobID = B.JobID
LEFT JOIN JobApplicationStatus AS C ON B.ApplicationStatusID = C.JobApplicationStatusID
WHERE A.BrandID = 1
GROUP BY B.JobID, B.LocationID, B.ApplicationStatusID

它有效,禁止两件事: -

  • 我不确定如何获得总计数(或者我可以在以后执行此服务器端)
  • 它没有显示计数为0的结果。

有人可以建议如何获得每个位置的每个工作的4个状态,即使它是0吗?我已经看到关于左连接的其他线程的各种建议,但我还没有成功。

提前致谢!

编辑 - 显示工作表示例数据

假设工作地点是ID号为5,6和7的地址表。

Job
+ ------ + ---------- + ------------------- +
| Job ID | Job Title  | Description         |
+ ------ + ---------- + ------------------- +
| 1      | Developer  | My Nice Description |
| 2      | Full Stack | Another Description |
+ ------ + ---------- + ------------------- +

Job Location
+ -- + ------ + ----------- +
| ID | Job ID | Location ID |
+ -- + ------ + ----------- +
| 1  | 1      | 5           |
| 2  | 1      | 6           |
| 3  | 2      | 5           |
| 4  | 2      | 6           |
| 5  | 2      | 7           |
+ -- + ------ + ----------- +

Job Application
+ -- + ------- + ------ + ----------- + --------------------- +
| ID | User ID | Job ID | Location ID | Application Status ID |
+ -- + ------- + ------ + ----------- + --------------------- +
| 1  | 1       | 1      | 5           | 1                     |
| 2  | 1       | 1      | 6           | 2                     |
| 3  | 2       | 1      | 5           | 1                     |
| 4  | 2       | 2      | 7           | 4                     |
+ -- + ------- + ------ + ----------- + --------------------- +

在作业申请状态表中: -

  • 用户1申请位置5的职位1,申请状态为1。

  • 用户1也适用于位置6的作业1,其申请状态为2。

  • 用户2申请位置5的职位1,申请状态为1。

  • 用户2也在位置7申请工作2,申请状态为4。

我在这里寻找的是一个返回以下内容的查询: -

Result
+ --- + -------- + ------ + ----- +
| Job | Location | Status | Count |
+ --- + -------- + ------ + ----- +
| 1   | 5        | 1      | 2     |
| 1   | 5        | 2      | 0     |
| 1   | 5        | 3      | 0     |
| 1   | 5        | 4      | 0     |
| 1   | 6        | 1      | 0     |
| 1   | 6        | 2      | 1     |
| 1   | 6        | 3      | 0     |
| 1   | 6        | 4      | 0     |
+ --- + -------- + ------ + ----- +

依此类推,应显示0。计数是该状态编号中的作业应用计数。有4个状态编号,在名为JobApplicationStatus的连接表中设置。

4 个答案:

答案 0 :(得分:3)

你快到了。由于即使Job Application中没有对应关系也需要所有状态,因此您需要使用cross join

此外,在这种情况下,因为会有一个"链接"对于每个状态,您需要使用SUM和CASE场景而不是COUNT。

SQL Fiddle

MS SQL Server 2014架构设置

CREATE TABLE Job
    ([JobID] int, [JobTitle] varchar(10), [Description] varchar(23), [BrandID] int)
;

INSERT INTO Job
    ([JobID], [JobTitle], [Description], [BrandID])
VALUES
    (1, 'Developer', 'My nice description', 1),
    (2, 'Full Stack', 'Another job description', 1)
;


CREATE TABLE JobApplication
    ([ID] int, [UserID] int, [JobID] int, [LocationID] int, [ApplicationStatusID] int)
;

INSERT INTO JobApplication
    ([ID], [UserID], [JobID], [LocationID], [ApplicationStatusID])
VALUES
    (1, 1, 1, 5, 1),
    (2, 1, 1, 6, 2),
    (3, 2, 1, 5, 1),
    (4, 2, 2, 7, 4)
;


CREATE TABLE JobApplicationStatus
    ([JobApplicationStatusID] int, [Description] varchar(11))
;

INSERT INTO JobApplicationStatus
    ([JobApplicationStatusID], [Description])
VALUES
    (1, 'New'),
    (2, 'In Progress'),
    (3, 'Hold'),
    (4, 'Closed')
;

查询1

SELECT B.JobID, B.LocationID, C.JobApplicationStatusID, 
sum(case when B.ApplicationStatusID = C.JobApplicationStatusID then 1 else 0 end) AS CountOfResults
FROM Job AS A
JOIN JobApplication AS B ON A.JobID = B.JobID
cross join JobApplicationStatus AS C
WHERE A.BrandID = 1
GROUP BY B.JobID, B.LocationID, C.JobApplicationStatusID

<强> Results

| JobID | LocationID | JobApplicationStatusID | CountOfResults |
|-------|------------|------------------------|----------------|
|     1 |          5 |                      1 |              2 |
|     1 |          5 |                      2 |              0 |
|     1 |          5 |                      3 |              0 |
|     1 |          5 |                      4 |              0 |
|     1 |          6 |                      1 |              0 |
|     1 |          6 |                      2 |              1 |
|     1 |          6 |                      3 |              0 |
|     1 |          6 |                      4 |              0 |
|     2 |          7 |                      1 |              0 |
|     2 |          7 |                      2 |              0 |
|     2 |          7 |                      3 |              0 |
|     2 |          7 |                      4 |              1 |

已编辑:

有一种方法可以让作业ID没有应用程序。只需将JOIN JobApplication更改为LEFT OUTER JOIN JobApplication,然后使用B.JobID更改SELECTGROUP BY子句中的A.JobID

答案 1 :(得分:0)

此类问题通常涉及cross join,然后以某种方式引入结果(left join / group by)或子查询。这表明:

SELECT j.JobID, l.LocationID, ja.ApplicationStatusID,
       COUNT(ja.JobID) AS CountOfResults
FROM Job j CROSS JOIN
     Location l LEFT JOIN
     JobApplication ja
     ON j.JobID = jb.JobID AND
        l.LocationId = jb.LocationId
WHERE j.BrandID = 1
GROUP BY j.JobID, l.LocationID, ja.ApplicationStatusID

答案 2 :(得分:0)

使用count可以在没有大小写的情况下获得相同的结果。我想出了Fabien几乎所有的解决方案。交叉连接是关键所在。

<强>设定:

DECLARE @Job TABLE(
        JobId tinyint,
        JobTitle varchar(55),
        JobDescription varchar(55)
);

INSERT INTO @Job 
    VALUES 
    (1,'Developer','My nice description'), 
    (2,'Full Stack','Another job description');

DECLARE @JobLocation TABLE(
    Id tinyint,
    JobId tinyint,
    LocationId tinyint

);

INSERT INTO @JobLocation 
    VALUES 
    (1,1,5), 
    (2,1,6),
    (3,2,5),
    (4,2,6),
    (5,2,7);

DECLARE @JobApplication TABLE(
    Id tinyint,
    UserId tinyint,
    JobId tinyint,
    LocationId tinyint,
    ApplicationStatusId tinyint
);

INSERT INTO @JobApplication
    VALUES
    (1,1,1,5,1),
    (2,1,1,6,2),
    (3,2,1,5,1),
    (4,2,2,7,4);
--End sample data

DECLARE @JobStatus TABLE(
    StatusId int,
    ApplicationStatus varchar(55)
);

INSERT INTO @JobStatus
    VALUES
    (1, 'Application Received'),
    (2, 'Application Review Started'),
    (3, 'Application In Final Review'),
    (4, 'Application Closed');

<强>查询:

select job.JobId,
       jl.LocationId,
       js.StatusId,
       count(ja.Id) as StatusCount

from @Job job
    cross apply @JobStatus js 
    inner join @JobLocation jl on job.JobId = jl.JobId
    left join @JobApplication ja on job.JobId = ja.JobId 
        and ja.LocationId = jl.LocationId 
        and ja.ApplicationStatusId = js.StatusId
where job.JobId = 1 -- filter for single job
group by job.jobId, jl.LocationId, js.StatusId
order by job.JobId, jl.LocationId, js.StatusId

答案 3 :(得分:0)

Fabien的回答很好,你应该把那一个标记为答案。但我只是想补充一点,你可以通过使用公用表表达式和相关子查询与他的查询来获得你想要的总和

; with 
    StatusCounts as (
        SELECT  B.JobID, B.LocationID, C.JobApplicationStatusID, 
                sum(case when B.ApplicationStatusID = C.JobApplicationStatusID then 1 else 0 end) AS CountOfResults
            FROM Job AS A
            JOIN JobApplication AS B ON A.JobID = B.JobID
            cross join JobApplicationStatus AS C
            WHERE A.BrandID = 1
            GROUP BY B.JobID, B.LocationID, C.JobApplicationStatusID
    )
select  *,
        (select SUM(CountOfResults)
            from StatusCounts SQ
            where SQ.JobID = D.JobID
                and SQ.LocationID = D.LocationID
        ) as TotalSum
    from StatusCounts D