You need to use UNION ALL for this. (Avoid plain UNION because it does extra work to deduplicate its result set.) Yes, it makes for an unpleasantly verbose query.
You'll end up writing code to create a query that looks like this.
SELECT userid, 'table_1' table_name FROM table1
UNION ALL
SELECT userid, 'table_2' table_name FROM table2
UNION ALL
SELECT userid, 'table_3' table_name FROM table3
...
There is some good news buried here. If you create a view containing this dirty great query ...
CREATE OR REPLACE VIEW all_userids
SELECT userid, 'table_1' table_name FROM table1
UNION ALL
SELECT userid, 'table_2' table_name FROM table2
UNION ALL
SELECT userid, 'table_3' table_name FROM table3
then do queries against the view like
SELECT userid FROM all_userids WHERE table_name = 'table_2'
the query optimizer knows what you're doing and avoids reading all the tables.
You can write a program to query the information_schema and generate this dirty great UNION ALL query. It's probably easiest to do that in a host language like Java / C# / php / whatever.
But you can also do it in SQL. Figure out how to write a query whose output is a query. Something like this might work. It uses STRING_AGG(val, separator) to generate the various SELECTs with UNION ALL between them. It then uses sp_executesql to run the query.
DECLARE @q NVARCHAR(MAX);
SELECT N'CREATE OR REPLACE VIEW all_userids ' +
STRING_AGG
(N'SELECT userid, ''' + table_name + N''' table_name FROM ' + table_name ,
N' UNION ALL ') + N';'
INTO @q
FROM information_schema.columns
WHERE column_name = 'userid';
EXECUTE sp_executesql @q;
This is not, repeat not, debugged.