Формат запроса T SQL для построения динамической таблицы - PullRequest
0 голосов
/ 05 октября 2018

Мне нужна помощь в создании запроса для получения результатов, которые я ищу.

Я создаю приложение для отслеживания посещаемости сотрудников на основе существующей таблицы базы данных.Пользователь выбирает дату или группу дат из календаря (скажем, 10/1/18, 10/2/18 и 10/3/18).Они нажимают кнопку «Отправить», и мне нужно сгенерировать таблицу, которая отображает каждого сотрудника с галочкой в ​​столбце даты, если они были там в тот день.

Таблица называется «История» и имеет 2 основных столбца: EmployeeID;и TransactionDate.Каждый раз, когда сотрудник проходит через дверь (включая вход), создается историческая транзакция (значки NFC), которая добавляет новую строку с идентификатором сотрудника и отметкой даты / времени.У каждого сотрудника, по-видимому, есть несколько ударов в день, но все, что мне действительно нужно, это знать, есть ли один раз на этот день.Я публикую фотографии того, как выглядит таблица и как должны выглядеть результаты запроса, и моя таблица должна выглядеть ...

Таблица: enter image description here

Результаты: enter image description here

Я мог бы сделать запрос вроде:

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

и отсортировать его программно, ноЯ чувствую, что есть гораздо более эффективный способ запрашивать результаты, которые я ищу.

Любая помощь очень ценится.Дайте мне знать, если мне нужно дать лучшее объяснение.

Ответы [ 5 ]

0 голосов
/ 12 октября 2018

с использованием созданной таблицы @TS:

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

предполагается, что даты не всегда совпадают

0 голосов
/ 12 октября 2018

Итак, я нашел один способ получить результаты, которые я ищу.

Сначала мне нужно получить различные идентификаторы сотрудников и сохранить их во временной таблице:

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

Длябольше дат, я просто добавляю новый столбец с указанием даты в качестве заголовка.Спасибо всем за помощь в этом вопросе.

0 голосов
/ 05 октября 2018

То, что вы хотите сделать, называется 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

Вы можете проверить документацию sql на PIVOT и UNPIVOT на PIVOT, UNPIVOT SQL Кроме того, если вы хотите что-то более динамичное для ваших полей трансдата, вы можете проверить Динамический свод

0 голосов
/ 05 октября 2018

Вы сказали: "все, что мне действительно нужно, это знать, есть ли для этого дня одно касание."

Ответ - совокупный запрос

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, но мне нравится кодировать, я покажу вам рабочий код как это можно сделать программно.Ниже приведено несколько фрагментов кода, которые могут быть объединены в один

-- 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

. В результате получается: *

enter image description here

Не самый быстрый или самый эффективный код, но, безусловно, весело.И, 1 - это полностью динамично.2 - если вы посмотрите на результат, а теперь возвращаясь к проблеме, о которой я упоминал ранее, вы можете присоединить эту таблицу к полному списку сотрудников и получить полный набор данных, включая сотрудников, которые не совершали пролистывание в течение выбранного периода.

0 голосов
/ 05 октября 2018

Если столбцы сводной диаграммы фиксированы, вы можете использовать это

Редактировать - Я полагаю, это также может быть динамическим, поскольку вы можете динамически создавать оператор SQL.

Документы Microsoft: ОТ - Использование PIVOT и UNPIVOT

Пример

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