From 7beb71d208955f8badef270c67eb47f3caac1f96 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Tue, 4 Nov 2025 09:13:45 -0500 Subject: [PATCH] clarify Core / ORM insert parameter behaviors it seems to have gotten lost in our newer docs that we're looking at the first dict only for core insert. add sections to both INSERT tutorials explaining this difference references: #12962 Change-Id: Id2e6c7e7db57fba6aa7838d5c3c65dea1939445a --- doc/build/orm/queryguide/dml.rst | 9 +++++++++ doc/build/tutorial/data_insert.rst | 16 ++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/doc/build/orm/queryguide/dml.rst b/doc/build/orm/queryguide/dml.rst index 91fe9e7741..80c7d3b14f 100644 --- a/doc/build/orm/queryguide/dml.rst +++ b/doc/build/orm/queryguide/dml.rst @@ -79,6 +79,15 @@ or :func:`_orm.mapped_column` declarations, as well as with the **ORM mapped attribute name** and **not** the actual database column name, if these two names happen to be different. +.. tip:: ORM bulk INSERT **allows each dictionary to have different keys**. + The operation will emit multiple INSERT statements with different VALUES + clauses for each set of keys. This is distinctly different from a Core + :class:`_sql.Insert` operation, which as introduced at + :ref:`tutorial_core_insert_values_clause` only uses the **first** dictionary + in the list to determine a single VALUES clause for all parameter sets. + + + .. versionchanged:: 2.0 Passing an :class:`_dml.Insert` construct to the :meth:`_orm.Session.execute` method now invokes a "bulk insert", which makes use of the same functionality as the legacy diff --git a/doc/build/tutorial/data_insert.rst b/doc/build/tutorial/data_insert.rst index d0f6b236d5..843bd761e6 100644 --- a/doc/build/tutorial/data_insert.rst +++ b/doc/build/tutorial/data_insert.rst @@ -157,6 +157,22 @@ method in conjunction with the :class:`_sql.Insert` construct, the will be expressed in the VALUES clause of the :class:`_sql.Insert` construct automatically. +.. tip:: + + When passing a list of dictionaries to :meth:`_engine.Connection.execute` + along with a Core :class:`_sql.Insert`, **only the first dictionary in the + list determines what columns will be in the VALUES clause**. The rest of + the dictionaries are not scanned. This is both because within traditional + ``executemany()``, the INSERT statement can only have one VALUES clause for + all parameters, and additionally SQLAlchemy does not want to add overhead + by scanning every parameter dictionary to verify each contains the identical + keys as the first one. + + Note this behavior is distinctly different from that of an :ref:`ORM + enabled INSERT `, introduced later in this tutorial, + which performs a full scan of parameter sets in terms of an ORM entity. + + .. deepalchemy:: Hi, welcome to the first edition of **Deep Alchemy**. The person on the -- 2.47.3