Static batching essentially is the same as combining the objects manually. This is only viable for objects that do not move. Unity automatically performs static batching when loading a scene that contains objects which are marked as static for batching. Once batched you can no longer move the objects as they are now part of a larger mesh.
For procedural generated levels you can use the StaticBatchingUtility to manually batch objects together at runtime.
Dynamic batching is different. It only applies to objects that meet several criteria:
Only small meshes are supported. That means the mesh can’t have more than 900 vertex attributes. A single attribute is for example the vertex position. Others are a vertex normal, uv coordinates, color, tangent, … So if your mesh vertices define a position, a normal and texture coordinates the mesh can only have 300 vertices. If the mesh has more attributes per vertex the number gets even smaller.
The objects shouldn’t use different scaling or they might not be batched.
Objects are not being batched if you use lightmapping and the objects are located on different lightmaps.
The objects can’t use multi-pass shaders as they generally don’t batch.
The objects need to be “close together”.
Dynamic batching is done each frame and has some performance overhead on the CPU. It’s ment to batch several small objects (like many flying bullets).
The most efficient batching is static batching. It has some memory overhead due to the combined mesh instance. Dynamic batching is only useful in special cases. It’s nice to have but it’s difficult to rely on dynamic batching without fine tuning the objects you want to batch together.
Static batching works great. It probably has a larger memory overhead over manually creating a single mesh, but it saves you from manually combining meshes. It allows easy modular level design and good performance at runtime out of the box.