【问题标题】:T SQL Query Format to Build Dynamic TableT SQL查询格式构建动态表
【发布时间】:2018-10-05 19:47:28
【问题描述】:

我需要帮助来创建查询以生成我正在寻找的结果。

我正在创建一个应用程序来根据现有数据库表跟踪员工出勤情况。用户从日历中选择一个日期或一组日期(比如10/1/1810/2/1810/3/18)。他们点击提交,我需要生成一个表格,显示每位员工,如果他们当天在场,则在日期列中带有复选标记。

该表名为 History,有 2 个主要列:EmployeeID;和交易日期。每次员工走过一扇门(包括入口)时,都会创建一个历史事务(NFC 徽章),该事务会添加一个带有员工 ID 和日期/时间戳的新行。每个员工每天可能有几次滑动,但我真正需要知道的是那天是否有一次滑动。我正在发布表格的外观以及查询结果和我的表格的外观的图片...

表格:

结果:

我可以这样查询:

select employeeid, TranDate from History 
where (CAST(trandate as DATE) = '2018-10-1' or CAST(trandate as DATE) = '2018-10-2' or CAST(trandate as DATE) = '2018-10-3' ) 
order by employeeid, TranDate

并以编程方式对其进行排序,但我觉得有一种更有效的方法来查询我正在寻找的结果。

非常感谢任何帮助。如果需要更好的解释,请告诉我。

【问题讨论】:

  • @Jan Srsen 感谢您的编辑提示
  • 试试看这个帖子,和动态数据透视表有关stackoverflow.com/questions/10404348/…
  • 要记住的一点:SQL 语言对于在查询编译时之前 它开始执行任何东西。它将接受列数由数据决定的任何类型的查询。即使SELECT * 查询也会这样做,因为查询使用的表中列的数量和类型是已知的。您在这里应该没问题,因为用户通过从日历中选择日期来创建已知范围,但对于未来的枢轴来说,这是要记住的。

标签: sql sql-server sql-server-2008 sql-server-2012


【解决方案1】:

你想做的就是所谓的pivot

尝试使用此代码

SELECT employeeid
      ,case when [2018-10-1] > 0 then '1' else '' end as [2018-10-1]
      ,case when [2018-10-2] > 0 then '1' else '' end as [2018-10-2]
      ,case when [2018-10-3] > 0 then '1' else '' end as [2018-10-3]
FROM(
SELECT employeeid
     , TranDate
     , 1 as num 
FROM History 
WHERE (CAST(trandate as DATE) = '2018-10-1' 
       or CAST(trandate as DATE) = '2018-10-2' 
       or CAST(trandate as DATE) = '2018-10-3' ) 
)
PIVOT(
COUNT(num)
FOR TranDate IN ([2018-10-1],[2018-10-2],[2018-10-3])
) as pvt

您可以在PIVOT, UNPIVOT SQL 上查看关于 PIVOT 和 UNPIVOT 的 sql 文档,此外,如果您想要更动态的转换字段,您可以查看 Dynamic Pivot

【讨论】:

  • 我在顶部 SELECT employeeid, [2018-10-1], [2018-10-2], [2018-10-3] 处收到“无效的列名 'employeeid'”错误。当我取出employeeID并运行它时,所有列看起来都正确。
  • 对不起,我忘记了一个细节,再试一次,这有点棘手,因为你的历史表中没有要计算的东西,这就是为什么我添加 1 作为虚拟数字
  • 它是如此接近...打印我正在寻找的内容,但我在每一行和每一列中都得到 0,除了employeeid 列。对于我缺乏 SQL 技能,我深表歉意。直到今天我才听说过支点
  • 我再次编辑,我添加了一个控制0和其他值的案例,再试一次。
【解决方案2】:

你说:“我真正需要的是知道那天是否有单次滑动。”

答案 - 聚合查询

select employeeid, TranDate, count(TranDate) as count
from History 
where -- look below
Group by employeeid, TranDate

WHERE 中输入什么? - 依靠。如果你想要月份,那就做

TranDate between '2018-10-1' and '2018-10-31 23:59:59.999'

如果您想要特定日期,请执行

CAST(trandate as DATE) in ('2018-10-1', '2018-10-11', '2018-10-11')

在后一种情况下,您必须动态构建它

但在这种情况下,您将面临一个新问题 - 如果您的人从未进过门怎么办?此人不会出现在列表中。然后你需要letf join这个有一个包含所有员工的表

现在因为我不是 TSql 超级大师,但我喜欢编码,我向您展示工作代码如何以编程方式完成。下面是几个可以组合成一个的sn-ps代码

-- SETUP
create table recs (id int, dt date );
insert into recs values(1, '2018-10-1');
insert into recs values(1, '2018-10-1');
insert into recs values(2, '2018-10-2');
insert into recs values(2, '2018-10-2');
insert into recs values(2, '2018-10-3');
insert into recs values(3, '2018-10-3');
insert into recs values(3, '2018-10-3');
insert into recs values(3, '2018-10-4');
insert into recs values(3, '2018-10-1'); 


-- Prepare Date Grid table
DECLARE crs_dt CURSOR FOR SELECT dt FROM recs group by dt order by dt;
declare @createTbl varchar(1200) = 'create table tbl (id int, ';
declare @fetched varchar(20);

OPEN crs_dt;  
FETCH NEXT FROM crs_dt into @fetched;  

WHILE @@FETCH_STATUS = 0  
BEGIN  
   set @createTbl = @createTbl + '['+@fetched+'] int'
   FETCH NEXT FROM crs_dt into @fetched;
   if @@FETCH_STATUS = 0
   begin set @createTbl = @createTbl + ','; end
END  

set @createTbl = @createTbl + ')';

CLOSE crs_dt;  
DEALLOCATE crs_dt;

select @createTbl; -- just a test
execute (@createTbl)
SELECT * FROM tbl; -- just a test


-- LOAD date grid table with data
DECLARE crs_rec CURSOR FOR 
SELECT id, dt, FORMAT ( dt, 'yyyy-MM-dd' ) colName, 
(case count(dt)  when 0 then 0 else 1 end) cnt
FROM recs group by id, dt order by dt;

declare @createInsert varchar(1200);
declare @id int, @dt date, @colName varchar(20),@yesNo int;

OPEN crs_rec;  
FETCH NEXT FROM crs_rec into @id, @dt, @colName, @yesNo;  

WHILE @@FETCH_STATUS = 0  
BEGIN
   if exists(select 1 from tbl where id = @id)
       set @createInsert = 'update tbl set ['+@colName+']='+cast(@yesNo as varchar(1))+ ' where id='+ cast(@id as varchar(1000));
   else  
       set @createInsert = 
           'insert into tbl (id,['+@colName+']) values ('+cast(@id as varchar(1000))+','+cast(@yesNo as varchar(1))+')';
   select @createInsert; -- just a test
   execute (@createInsert);

   FETCH NEXT FROM crs_rec into  @id, @dt, @colName, @yesNo;
END  
CLOSE crs_rec;  
DEALLOCATE crs_rec;

commit;

-- Lets Load data 
SELECT * FROM tbl

结果是……

不是最快或最高效的代码,但肯定很有趣。而且,1-这是完全动态的。 2-如果你看结果,现在回到我之前提到的问题,你可以将此表加入到完整的员工列表中,并获取整组数据,包括在所选时间段内没有刷卡的员工。

【讨论】:

  • 在他知道是否对所选日期进行了滑动之后,他还需要按照他的显示方式显示它。
【解决方案3】:

所以我想出了一种方法来获得我正在寻找的结果。

首先我需要获取不同的员工 ID 并将它们存储到临时表中:

create table #TempTable
(
    EmployeeID varchar(25)
)

insert into #TempTable (EmployeeID)

select distinct employeeID from History order by employeeID;

从这里,我可以将我的临时表加入到我的历史表中,并准确地选择我要查找的内容:

SELECT distinct M.EmployeeID AS Employee, 
(SELECT CASE WHEN EXISTS
(SELECT 1 FROM HISTORY WHERE TAGID = M.EmployeeID AND (CAST(TRANDATE AS DATE) = '10/12/2018')) 
THEN CAST (1 AS BIT) ELSE CAST (0 AS BIT) END) AS '10/12/2018'           
FROM #TempTable AS M left JOIN HISTORY AS H ON M.EmployeeID = H.TAGID where m.EmployeeID = '000000000000000000000001' order BY Employee

最后删除临时表:

drop table #TempTable

对于更多日期,我只需添加一个新列,将日期作为标题。 感谢大家在这个问题上的帮助。

【讨论】:

    【解决方案4】:

    使用@T.S.创建表:

    create table recs (id int, dt date );
    insert into recs values(1, '2018-10-1');
    insert into recs values(1, '2018-10-1');
    insert into recs values(2, '2018-10-2');
    insert into recs values(2, '2018-10-2');
    insert into recs values(2, '2018-10-3');
    insert into recs values(3, '2018-10-3');
    insert into recs values(3, '2018-10-3');
    insert into recs values(3, '2018-10-4');
    insert into recs values(3, '2018-10-1'); 
    
    declare @dts as varchar(max) =''
    declare @dtsSelect as varchar(max) =''
    select  @dts =  @dts + ',[' +dt +']',
                 @dtsSelect =  @dtsSelect + ',Case WHEN [' +dt+']>0 then 1 else 0 end ['+dt+']'
    From (select distinct cast(dt as varchar(100)) dt from recs) recs
    //we use some variables to create columns should apear in pivot list and 
    // also to check if we have a value for column then put 1 instead
    set @dts = Stuff(@dts,1,1,'')
    set @dtsSelect = Stuff(@dtsSelect,1,1,'')
    //delete the first comma
    
    declare @sql nvarchar(max) = '
    select id,'+@dtsSelect+' from recs 
    pivot(Count(dt) For dt in('+@dts+'))PV
    
    '
    exec sp_executesql @stme = @sql
    

    假设日期并不总是相同

    【讨论】:

      【解决方案5】:

      如果枢轴列是固定的,您可以使用它

      编辑 - 我猜它也可以是动态的,因为您可以动态创建 SQL 语句。

      Microsoft Docs : FROM - Using PIVOT and UNPIVOT

      例子

      select * from History
      PIVOT  
      (
          COUNT(TranDate)
          FOR TranDate IN ([2018-10-01], [2018-10-02], [2018-10-03])  
      ) AS PivotTable;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-08-04
        • 2011-11-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-09-24
        相关资源
        最近更新 更多