我正在尝试在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
答案 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
等同于所需输出中的列。
使用LINQ2SQL和GroupBy 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)
我只是在玩耍,这就是我想出来的:
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
答案 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