SQL Server – Diviser un set de données en groupes de même taille avec des lignes randoms

Avoir un set de données de base qui est un set d'ID, FE

000 111 222 333 444 555 666 777 888 999 

et un jeu de keys

 1 - 20 2 - 40 3 - 40 

Le résultat devrait être un appariement random de l'set de données de base avec l'set de données de keys en fonction du pourcentage défini, ce qui signifie que 20% recevront 1, 40% recevront 2 et 40% recevront 3

FE – première manche

 000 - 1 111 - 2 222 - 3 333 - 1 444 - 2 555 - 3 666 - 2 777 - 3 888 - 2 999 - 3 

deuxième passage

 000 - 2 111 - 3 222 - 2 333 - 3 444 - 2 555 - 3 666 - 1 777 - 3 888 - 2 999 - 1 

etc.

Utilisation de SQL Server 2014

Pensé des solutions qui impliquent des loops et des cusrors et des tables de temp mais je me demandais s'il y a une solution «plus propre» et probablement plus efficace …

Des idées?

Voici une façon d'utiliser la table des Numbers .

 ;WITH base_dataset AS (SELECT *, Row_number()OVER(ORDER BY id) AS rn FROM (VALUES (000), (111), (222), (333), (444), (555), (666), (777), (888), (999)) tc (ID)), keys AS (SELECT * FROM (VALUES (1,20), (2,50), (3,30)) tc(val, per)), num_gen AS (SELECT 1 AS num, Count(1) AS cnt FROM base_dataset UNION ALL SELECT num + 1, cnt FROM num_gen WHERE num < cnt) SELECT Id,val FROM (SELECT Row_number()OVER(ORDER BY newid()) rn, val FROM num_gen n JOIN keys k ON n.num <= (k.per/100.0) * cnt) a JOIN base_dataset d ON d.rn = a.rn 

J'ai utilisé le Recursive CTE pour générer des nombres, vous pouvez créer une table de nombres dans la database et l'utiliser

Vous pouvez le faire en utilisant row_number() et une join . L'idée est d'accumuler les "valeurs" keys (quelle que soit la deuxième colonne) puis de les normaliser entre 0 et 1. Faites de même pour un ordre de rang random et (essentiellement) between pour le mappage entre les tables:

 with k as ( select k.*, sum(val) over (order by id) * 1.0 / sum(sum(val)) over () as cume_sumval from keys k ) select d.*, k.id from (select d.*, row_number() over (order by newid()) - 1 as seqnum, count(*) as cnt from dataset d ) d join k on seqnum >= (k.cume_sumval - v) * cnt and seqnum < (k.cume_sumval) * cnt;