I partially have a solution using a union query in proc sql
which contains subqueries to achieve the anonymous persons name and number.
But as you notice each person is manually entered into each select query. You would hate to do this for say 200 people in your dataset instead of the 5 you show in example. One possibility is to run insert queries mirroring the union's select queries:
proc sql;
create table AnonymousReport As
SELECT CASE WHEN t1.name = 'Alice' THEN t1.name ELSE CATS('Person',
(SELECT count(name) + 1 From have t2
WHERE t2.name <= t1.name AND t1.name ne t2.name)) END As RptName,
t1.Col1Number, t1.Col1Pct, t1.Col2Number, t1.Col2Pct, 'Alice' As ReportToWhom
FROM have t1
UNION ALL
SELECT CASE WHEN t1.name = 'Bob' THEN t1.name ELSE CATS('Person',
(SELECT count(name) + 1 From have t2
WHERE t2.name < t1.name AND t1.name ne t2.name)) END As RptName,
t1.Col1Number, t1.Col1Pct, t1.Col2Number, t1.Col2Pct, 'Bob' As ReportToWhom
FROM have t1
UNION ALL
SELECT CASE WHEN t1.name = 'Carol' THEN t1.name ELSE CATS('Person',
(SELECT count(name) + 1 From have t2
WHERE t2.name < t1.name AND t1.name ne t2.name)) END As RptName,
t1.Col1Number, t1.Col1Pct, t1.Col2Number, t1.Col2Pct, 'Carol' As ReportToWhom
FROM have t1
UNION ALL
SELECT CASE WHEN t1.name = 'Dave' THEN t1.name ELSE CATS('Person',
(SELECT count(name) + 1 From have t2
WHERE t2.name < t1.name AND t1.name ne t2.name)) END As RptName,
t1.Col1Number, t1.Col1Pct, t1.Col2Number, t1.Col2Pct, 'Dave' As ReportToWhom
FROM have t1
UNION ALL
SELECT CASE WHEN t1.name = 'Erin' THEN t1.name ELSE CATS('Person',
(SELECT count(name) + 1 From have t2
WHERE t2.name < t1.name AND t1.name ne t2.name)) END As RptName,
t1.Col1Number, t1.Col1Pct, t1.Col2Number, t1.Col2Pct, 'Erin' As ReportToWhom
FROM have t1;
quit;
Output Dataset. From here export each person's individual report by the last column ReportToWhom
RptName Col1Number Col1Pct Col2Number Col2Pct ReportToWhom
Alice 4 15% 8 20% Alice
Person2 8 30% 6 15% Alice
Person3 4 15% 8 20% Alice
Person4 4 15% 8 20% Alice
Person5 4 15% 8 20% Alice
Person1 4 15% 8 20% Bob
Bob 8 30% 6 15% Bob
Person3 4 15% 8 20% Bob
Person4 4 15% 8 20% Bob
Person5 4 15% 8 20% Bob
...
One possible solution is to use a concatenated insert SQL query across all rows of dataset:
data concat;
set have;
length reptAll $3200;
by name;
retain reptAll;
if first.name then reptAll = "";
unionSQL = "INSERT INTO AnonymousReport (RptName, Col1Number, Col1Pct, Col2Number, Col2Pct, ReportToWhom)
SELECT CASE WHEN t1.name = '" || name || "' THEN t1.name
ELSE CATS('Person', (SELECT count(name) + 1 From have t2
WHERE t2.name <= t1.name AND t1.name ne t2.name)) END As RptName,
t1.Col1Number, t1.Col1Pct, t1.Col2Number, t1.Col2Pct, '" || name || "' As ReportToWhom
FROM have t1";
reptAll = catx('; ', reptAll, unionSQL) ;
call symput('query', reptAll);
if last.name then output;
run;
And then pass string into proc sql
macro:
%macro runsql;
proc sql;
&query;
quit;
%mend runsql;
%runsql;
In R, I could do this in seconds with its paste
/for
loop/apply
functions but SAS syntax is another world!