#sql-server #loops #random #cursor #unique
#sql-сервер #циклы #Случайный #курсор #уникальный
Вопрос:
Я пытаюсь сгенерировать непоследовательные уникальные выводы для группы новых сотрудников. Вот как это должно работать:
- Курсор должен извлекать всех новых сотрудников из таблицы B
- Создавайте новые уникальные контакты для каждого нового сотрудника
- Проверьте, существует ли вновь созданный вывод в таблице A
- Если да, повторяйте шаг 2, пока мы не получим уникальный PIN-код для всех новых сотрудников
- Вставьте новый номер сотрудника и контакты в таблицу A
В таблице A поля PIN, Employee_Number, Date
Я попытался использовать курсор для вставки каждого нового emp и его вывода один за другим, чтобы он проверял уникальность при каждой вставке:
Это выполняется только один раз и останавливается, может кто-нибудь сказать мне, чего мне не хватает
DECLARE
@EmpNo VARCHAR(50),
@Pin INT,
@today DATETIME,
@Upper INT,
@Lower INT,
SET @Lower = 100000 ---- The lowest random number allowed
SET @Upper = 999999 ---- The highest random number allowed
DECLARE cur CURSOR FOR
SELECT
Employee_Number,
(ROUND(((@Upper - @Lower -1) * RAND() @Lower), 0))as PIN,
GETDATE()
FROM TableB
WHERE Employee_Number NOT IN (SELECT Employee_Number FROM TableA)
OPEN cur
FETCH NEXT FROM cur INTO @EmpNo, @Pin, @Today
WHILE @@FETCH_STATUS=0
BEGIN
INSERT INTO TableA (PIN, Employee_Number, Date)
SELECT @Pin, @EmpNo, @Today
FETCH NEXT FROM cur INTO @Pin, @EmpNo, @Today
END
CLOSE cur
DEALLOCATE cur
Ответ №1:
вам не нужен курсор. Также лучше использовать newid() для генерации случайного числа вместо rand()
INSERT INTO TableA (PIN, Employee_Number, Date)
SELECT pin = @Lower
( abs(convert(bigint,convert(varbinary(20),newid())))
% (@Upper - @Lower 1)
),
Employee_Number,
GETDATE()
FROM TableB
WHERE Employee_Number NOT IN (SELECT Employee_Number FROM TableA)
Редактировать :
для обработки уникального вывода решение без использования курсора состоит в том, чтобы сохранить таблицу всех возможных выводов.
create table pin
(
pin int primary key,
is_used bit default 0
)
вы генерируете вывод, используя рекурсивный cte
; with num as
(
-- anchor member
select pin = @Lower
union all
-- recursive member
select pin = pin 1
from num
where pin < @Upper
)
insert into pin (pin)
select pin
from num
затем, когда вам нужно назначить PIN-код сотруднику
; with emp as
(
select Employee_Number, rn = row_number() over (order by Employee_Number)
from TableA a
where not exists
(
select *
from TableB b
where b.Employee_Number = a.Employee_Number
)
),
pin as
(
-- get unused pin, order it randomly using newid()
select pin, rn = row_number() over (order by newid())
from pin
where is_used = 0
)
insert into #TableB (Employee_Number, pin)
select e.Employee_Number, p.pin
from emp e
inner join pin p on e.rn = p.rn
после этого обновите столбец is_used в таблице выводов
update p
set is_used = 1
from TableB b
inner join #pin p on b.pin = p.pin
where p.is_used = 0
Комментарии:
1. Спасибо за альтернативу. Этот запрос работает без создания повторяющегося вывода, однако выдает ошибку ограничения УНИКАЛЬНОГО КЛЮЧА, поскольку поле вывода в таблице a становится УНИКАЛЬНЫМ. Будет ли безопасно удалить эту уникальную ключевую функцию из поля вывода?
2. А также при вставке запрос должен также проверять таблицу A на любые повторяющиеся выводы
3. Почему PIN-код должен быть уникальным для всех сотрудников? на самом деле это не имеет смысла для меня
4. уникальный вывод усложнит запрос. Однако для обеспечения уникального вывода вы можете использовать ИДЕНТИФИКАТОР в столбце pin.
5. @AhmedSaeed, разве это не сделает вывод
sequential
?
Ответ №2:
Другой подход может быть следующим.
Примечание @EmpTable является демонстративным пустым (удалите @ для вашей среды)
1) cte0 генерирует базовую таблицу подсчета из 10 строк
2) cteR расширяет cte0 до 1000 строк и добавляет случайную строку (NewID())
3) cteI преобразует cteR в INT (не более 6 цифр)
4) Обновление применяется ко всем нулевым ВЫВОДАМ
Declare @EmpTable table (Employee_Number int,PIN int)
Insert Into @EmpTable values
(1,100288),(2,651701),(3,823700),
(4,NULL),(5,NULL)
Declare @Lower int=100000,@Upper int = 999999
;with cte0(N) As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N))
, cteR(R) As (Select Distinct R=cast(NewID() as varchar(50)) From cte0 N1, cte0 N2, cte0 N3, cte0 N4)
, cteI(R,N) As (Select Distinct R,cast(Left(cast(abs(cast(Hashbytes('MD5',R) as int)) as varchar(25)),6) as int) From cteR R)
Update @EmpTable Set PIN = B.N
From @EmpTable A
Join (
Select RowNr=(Select min(Employee_Number)-1 From @EmpTable Where PIN is NULL) Row_Number() over (Order By R)
,N
From cteI
Where N Between @Lower and @Upper
and N not in (Select Distinct PIN From @EmpTable Where PIN is not NULL)
) B on A.Employee_Number=B.RowNr and PIN is null
Select * from @EmpTable
Обновлен пустой
Employee_Number PIN
1 100288 -- Same as Original
2 651701 -- Same as Original
3 823700 -- Same as Original
4 118844 -- New Random PIN
5 677855 -- New Random PIN