【问题标题】:Querying awkwardly stored data查询笨拙存储的数据
【发布时间】:2016-12-02 20:59:58
【问题描述】:

为含糊的标题道歉,但这更像是在寻求建议而不是寻找直接答案。作为参考,我使用的是 SSMS 2014。

我正在使用一个不是我创建的数据库,并且我正在努力使用其当前格式的数据。我已经包含了下表的示例。

+-----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|DtID | ID | Mo1 | Mo2 | Tu1 | Tu2 | We1 | We2 | Th1 | Th2 | Fr1 | Fr2 |
+-----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| 565 | 12 |   0 |   1 |   0 |   1 |   1 |   0 |   0 |   0 |   1 |   0 |
| 565 | 13 |   0 |   0 |   1 |   1 |   1 |   1 |   0 |   0 |   0 |   0 |
| 565 | 14 |   0 |   1 |   1 |   1 |   0 |   0 |   1 |   1 |   1 |   0 |
| 565 | 15 |   1 |   0 |   0 |   0 |   0 |   1 |   1 |   0 |   0 |   1 |
| 572 | 12 |   0 |   0 |   1 |   1 |   0 |   0 |   0 |   0 |   0 |   0 |
| 572 | 13 |   1 |   0 |   0 |   0 |   0 |   0 |   1 |   1 |   0 |   1 |
| 572 | 14 |   0 |   0 |   1 |   0 |   0 |   0 |   1 |   1 |   0 |   0 |
| 572 | 15 |   0 |   0 |   0 |   0 |   0 |   0 |   1 |   1 |   0 |   0 |
+-----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+

DtID 是一周开始日期标识符,ID 是人员标识符,Mo1 代表星期一上午,Mo2 星期一下午等。1 表示参加该会话的人员。该表超过 60 万行。

我的问题是在尝试查询特定日期范围之间的数据时出现的,例如,我想在日期标识 566 和 575 之间查找当前出勤分数的总和。日期 566 将指从 565 和 575 开始的每周星期二指从 572 开始的每周星期四。

我会更喜欢下面格式的表格(以第 1 行为例)。

+-----+----+------+---------+
|DtID | ID | AMPM | Present |
+-----+----+------+---------+
| 565 | 12 |    1 |       0 |
| 565 | 12 |    2 |       1 |
| 566 | 12 |    1 |       0 |
| 566 | 12 |    2 |       1 |
| 567 | 12 |    1 |       1 |
| 567 | 12 |    2 |       0 |
| 568 | 12 |    1 |       0 |
| 568 | 12 |    2 |       0 |
| 569 | 12 |    1 |       1 |
| 569 | 12 |    2 |       0 |
+-----+----+------+---------+

这可能吗?有没有一种有效的方法来查询数据?我对 SQL 还很陌生,到目前为止,我几乎所有的学习都是通过解释这个站点上的问题和答案。正如我所说,我真的不知道我在寻找什么的答案,所以这里用户的集体建议和智慧对我非常有用。

非常感谢。

【问题讨论】:

  • 你是对的。第二种表示比第一种更有意义。我会争辩说现有的桌子设计是有缺陷的,但是嘿,我们必须使用我们所拥有的。有多种方法可以将其转换为所需的形式。您可以交叉连接到另一个表,将 1 行变成 10 行(这是您想要的) - 这是一个示例:stackoverflow.com/questions/40866952/…。您可以使用 tenunion all,但通常它们很难让查询规划器优化。
  • 虽然工作量很大,但如果数据库设计一直这么“笨拙”,而且你必须做很多报告,你可以建立一个数据仓库
  • 我认为第二种设计也有缺陷,因为它确实无法代表工作日。由于没有其他标准可供排序,因此无法判断第二个表中的行指的是哪一天。
  • 感谢您的 cmets,尼克。这一点我已经记在心里了,以后一定会作为类似情况的参考!

标签: sql sql-server tsql


【解决方案1】:

我认为您不想泄露当天的信息

select  DtID,ID,left(day,2) as day,right(day,1) as AMPM,Present 

from    t unpivot (Present for day in (Mo1,Mo2,Tu1,Tu2,We1,We2,Th1,Th2,Fr1,Fr2)) t

+------+----+-----+------+---------+
| DtID | ID | day | AMPM | Present |
+------+----+-----+------+---------+
| 565  | 12 | Mo  | 1    | 0       |
+------+----+-----+------+---------+
| 565  | 12 | Mo  | 2    | 1       |
+------+----+-----+------+---------+
| 565  | 12 | Tu  | 1    | 0       |
+------+----+-----+------+---------+
| 565  | 12 | Tu  | 2    | 1       |
+------+----+-----+------+---------+
| 565  | 12 | We  | 1    | 1       |
+------+----+-----+------+---------+
| 565  | 12 | We  | 2    | 0       |
+------+----+-----+------+---------+
| 565  | 12 | Th  | 1    | 0       |
+------+----+-----+------+---------+
| 565  | 12 | Th  | 2    | 0       |
+------+----+-----+------+---------+
| 565  | 12 | Fr  | 1    | 1       |
+------+----+-----+------+---------+
| 565  | 12 | Fr  | 2    | 0       |
+------+----+-----+------+---------+
| 565  | 13 | Mo  | 1    | 0       |
+------+----+-----+------+---------+
| 565  | 13 | Mo  | 2    | 0       |
+------+----+-----+------+---------+
| 565  | 13 | Tu  | 1    | 1       |
+------+----+-----+------+---------+
| 565  | 13 | Tu  | 2    | 1       |
+------+----+-----+------+---------+
| 565  | 13 | We  | 1    | 1       |
+------+----+-----+------+---------+
| 565  | 13 | We  | 2    | 1       |
+------+----+-----+------+---------+
| 565  | 13 | Th  | 1    | 0       |
+------+----+-----+------+---------+
| 565  | 13 | Th  | 2    | 0       |
+------+----+-----+------+---------+
| 565  | 13 | Fr  | 1    | 0       |
+------+----+-----+------+---------+
| 565  | 13 | Fr  | 2    | 0       |
+------+----+-----+------+---------+

【讨论】:

  • 这只是为了我的目的,我可以用它来找到我的解决方案。如果您注意到我的“首选”替代方案,日期信息不会因此丢失,因为日期标识会根据日期递增。例如星期二用 566 表示。
  • 我没有得到“首选”部分
  • 首选我指的是原始帖子中的表格,我觉得使用起来更舒服。我的数据集的实际列标题称为“RegIdentAM1”、“RegIdentAM2”等,在示例中我称之为“MoAM”、“TuAM”等。我刚刚使用了正确的函数来提取日期标识符,并将其添加到日期标识符中并减去一个返回正确日期的函数。对您的回复非常满意 - 我以前没有遇到过 unpivot,所以我很高兴现在可以使用它。
【解决方案2】:

如果我是你,我会努力重构数据。我能够在生产系统中做到这一点的一种方法是使用重命名技巧。首先创建一个新表,然后创建一个以原始表命名方式命名的视图。现有的报告和查询在根据性能要求被替换之前仍然有效。

与此同时,您可以使用更花哨的东西,例如 unpivot 或 join,但我一直发现联合解决方案易于设置、维护并且从未出现性能问题。你可能有不同的结果,但它很容易测试。

编辑: 我对一个包含 20,971,520 行的数据集进行了一些测试,该数据集是通过一遍又一遍地附加数据而创建的(Insert Temp Select * From Temp)。

Select * From vTemp - 1:28

Select DtID,ID,col,Present
   From Temp UnPivot (Present For Col In (Mo1,Mo2,Tu1,Tu2,We1,We2,Th1,Th2,Fr1,Fr2)) T - 1:30    

Select * From vTemp Where ID = 12  - :23

Select DtID,ID,col,Present
   From Temp UnPivot (Present For Col In (Mo1,Mo2,Tu1,Tu2,We1,We2,Th1,Th2,Fr1,Fr2)) T 
Where ID = 12 :24

总而言之,如果您喜欢枢轴,那么打字就会少得多。对于那些不喜欢 pivot 语法的人,您可以使用 Union 或 Union All 取决于您是否要删除重复项。

例如:

Drop Table Temp
Create Table Temp (DtID int, ID int, Mo1 bit, Mo2 bit, Tu1 bit, Tu2 bit, We1 bit, We2 bit, Th1 bit, Th2 bit, Fr1 bit, Fr2 bit)


Insert Temp Values ( 565 , 12 ,   0 ,   1 ,   0 ,   1 ,   1 ,   0 ,   0 ,   0 ,   1 ,   0 )
Insert Temp Values ( 565 , 13 ,   0 ,   0 ,   1 ,   1 ,   1 ,   1 ,   0 ,   0 ,   0 ,   0 )
Insert Temp Values ( 565 , 14 ,   0 ,   1 ,   1 ,   1 ,   0 ,   0 ,   1 ,   1 ,   1 ,   0 )
Insert Temp Values ( 565 , 15 ,   1 ,   0 ,   0 ,   0 ,   0 ,   1 ,   1 ,   0 ,   0 ,   1 )
Insert Temp Values ( 572 , 12 ,   0 ,   0 ,   1 ,   1 ,   0 ,   0 ,   0 ,   0 ,   0 ,   0 )
Insert Temp Values ( 572 , 13 ,   1 ,   0 ,   0 ,   0 ,   0 ,   0 ,   1 ,   1 ,   0 ,   1 )
Insert Temp Values ( 572 , 14 ,   0 ,   0 ,   1 ,   0 ,   0 ,   0 ,   1 ,   1 ,   0 ,   0 )
Insert Temp Values ( 572 , 15 ,   0 ,   0 ,   0 ,   0 ,   0 ,   0 ,   1 ,   1 ,   0 ,   0 )

Drop View vTemp
Go
Create View vTemp As
Select  DtID, ID, 'Monday' Day, 1 AmPm, Mo1 Present From Temp 
Union All
Select  DtID, ID, 'Monday' Day, 2 AmPm, Mo1 Present From Temp 
Union All
Select  DtID, ID, 'Tuesday' Day, 1 AmPm, Tu1 Present From Temp 
Union All
Select  DtID, ID, 'Tuesday' Day, 2 AmPm, Tu2 Present From Temp 
Union All
Select  DtID, ID, 'Wednesday' Day, 1 AmPm, We1 Present From Temp 
Union All
Select  DtID, ID, 'Wednesday' Day, 2 AmPm, We2 Present From Temp 
Union All
Select  DtID, ID, 'Thursday' Day, 1 AmPm, Th1 Present From Temp 
Union All
Select  DtID, ID, 'Thursday' Day, 2 AmPm, Th2 Present From Temp 
Union All
Select  DtID, ID, 'Friday' Day, 1 AmPm, Fr1 Present From Temp 
Union All
Select  DtID, ID, 'Friday' Day, 2 AmPm, Fr2 Present From Temp 

Go

Select * From vTemp Order By ID, DtID, Day, AmPm

【讨论】:

  • 他说的是查询而不是 DML。我不知道如何以他想要的方式表示数据,并且能够在不改变我强烈推荐的基本设计的情况下进行插入。
  • 那是性能测试吗? (1) 你重复了多少次? (2) 每次运行前您是否清理/填充了缓存? (3) 你是用服务器时间还是客户端时间?以及将select * 更改为select count(*) 怎么样?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-05
  • 2014-09-16
  • 2011-10-17
  • 2017-09-05
  • 1970-01-01
相关资源
最近更新 更多