I wrote some code a while ago that merges two records. Is not entirely dynamic, but whith macros you could easily use it for several records.
It works like this: The merge/2 function takes two records and converts them to lists together with the empty record for reference (the record type is defined at compile time, and must be. This is the "undynamic" part). These are then run through the generic function merge/4 which works with lists and takes elements from A if they are defined, otherwise from B if they are defined, or lastly from Default (which is always defined).
Here's the code (please excuse StackOverflow's poor Erlang syntax highlighting):
%%%----------------------------------------------------------------------------
%%% @spec merge(RecordA, RecordB) -> #my_record{}
%%% RecordA = #my_record{}
%%% RecordB = #my_record{}
%%%
%%% @doc Merges two #my_record{} instances. The first takes precedence.
%%% @end
%%%----------------------------------------------------------------------------
merge(RecordA, RecordB) when is_record(RecordA, my_record),
is_record(RecordB, my_record) ->
list_to_tuple(
lists:append([my_record],
merge(tl(tuple_to_list(RecordA)),
tl(tuple_to_list(RecordB)),
tl(tuple_to_list(#my_record{})),
[]))).
%%%----------------------------------------------------------------------------
%%% @spec merge(A, B, Default, []) -> [term()]
%%% A = [term()]
%%% B = [term()]
%%% Default = [term()]
%%%
%%% @doc Merges the lists `A' and `B' into to a new list taking
%%% default values from `Default'.
%%%
%%% Each element of `A' and `B' are compared against the elements in
%%% `Default'. If they match the default, the default is used. If one
%%% of them differs from the other and the default value, that element is
%%% chosen. If both differs, the element from `A' is chosen.
%%% @end
%%%----------------------------------------------------------------------------
merge([D|ATail], [D|BTail], [D|DTail], To) ->
merge(ATail, BTail, DTail, [D|To]); % If default, take from D
merge([D|ATail], [B|BTail], [D|DTail], To) ->
merge(ATail, BTail, DTail, [B|To]); % If only A default, take from B
merge([A|ATail], [_|BTail], [_|DTail], To) ->
merge(ATail, BTail, DTail, [A|To]); % Otherwise take from A
merge([], [], [], To) ->
lists:reverse(To).
Feel free to use it in any way you want.