Optimize history.get_last_state_changes query (#87554)

fixes undefined
pull/87634/head
J. Nick Koston 2023-02-07 07:50:44 -06:00 committed by GitHub
parent 4142f0d15d
commit 0a1d5c85ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 28 additions and 24 deletions

View File

@ -518,48 +518,52 @@ def state_changes_during_period(
def _get_last_state_changes_stmt( def _get_last_state_changes_stmt(
schema_version: int, number_of_states: int, entity_id: str | None schema_version: int, number_of_states: int, entity_id: str
) -> StatementLambdaElement: ) -> StatementLambdaElement:
stmt, join_attributes = lambda_stmt_and_join_attributes( stmt, join_attributes = lambda_stmt_and_join_attributes(
schema_version, False, include_last_changed=False schema_version, False, include_last_changed=False
) )
if schema_version >= 31: if schema_version >= 31:
stmt += lambda q: q.filter( stmt += lambda q: q.where(
(States.last_changed_ts == States.last_updated_ts) States.state_id
| States.last_changed_ts.is_(None) == (
select(States.state_id)
.filter(States.entity_id == entity_id)
.order_by(States.last_updated_ts.desc())
.limit(number_of_states)
.subquery()
).c.state_id
) )
else: else:
stmt += lambda q: q.filter( stmt += lambda q: q.where(
(States.last_changed == States.last_updated) | States.last_changed.is_(None) States.state_id
== (
select(States.state_id)
.filter(States.entity_id == entity_id)
.order_by(States.last_updated.desc())
.limit(number_of_states)
.subquery()
).c.state_id
) )
if entity_id:
stmt += lambda q: q.filter(States.entity_id == entity_id)
if join_attributes: if join_attributes:
stmt += lambda q: q.outerjoin( stmt += lambda q: q.outerjoin(
StateAttributes, States.attributes_id == StateAttributes.attributes_id StateAttributes, States.attributes_id == StateAttributes.attributes_id
) )
if schema_version >= 31:
stmt += lambda q: q.order_by( stmt += lambda q: q.order_by(States.state_id.desc())
States.entity_id, States.last_updated_ts.desc()
).limit(number_of_states)
else:
stmt += lambda q: q.order_by(
States.entity_id, States.last_updated.desc()
).limit(number_of_states)
return stmt return stmt
def get_last_state_changes( def get_last_state_changes(
hass: HomeAssistant, number_of_states: int, entity_id: str | None hass: HomeAssistant, number_of_states: int, entity_id: str
) -> MutableMapping[str, list[State]]: ) -> MutableMapping[str, list[State]]:
"""Return the last number_of_states.""" """Return the last number_of_states."""
start_time = dt_util.utcnow() entity_id_lower = entity_id.lower()
entity_id = entity_id.lower() if entity_id is not None else None entity_ids = [entity_id_lower]
entity_ids = [entity_id] if entity_id is not None else None
with session_scope(hass=hass) as session: with session_scope(hass=hass) as session:
stmt = _get_last_state_changes_stmt( stmt = _get_last_state_changes_stmt(
_schema_version(hass), number_of_states, entity_id _schema_version(hass), number_of_states, entity_id_lower
) )
states = list(execute_stmt_lambda_element(session, stmt)) states = list(execute_stmt_lambda_element(session, stmt))
return cast( return cast(
@ -568,7 +572,7 @@ def get_last_state_changes(
hass, hass,
session, session,
reversed(states), reversed(states),
start_time, dt_util.utcnow(),
entity_ids, entity_ids,
include_start_time_state=False, include_start_time_state=False,
), ),

View File

@ -330,7 +330,7 @@ def test_get_last_state_changes(hass_recorder):
start = dt_util.utcnow() - timedelta(minutes=2) start = dt_util.utcnow() - timedelta(minutes=2)
point = start + timedelta(minutes=1) point = start + timedelta(minutes=1)
point2 = point + timedelta(minutes=1) point2 = point + timedelta(minutes=1, seconds=1)
with patch( with patch(
"homeassistant.components.recorder.core.dt_util.utcnow", return_value=start "homeassistant.components.recorder.core.dt_util.utcnow", return_value=start

View File

@ -257,7 +257,7 @@ def test_get_last_state_changes(hass_recorder):
start = dt_util.utcnow() - timedelta(minutes=2) start = dt_util.utcnow() - timedelta(minutes=2)
point = start + timedelta(minutes=1) point = start + timedelta(minutes=1)
point2 = point + timedelta(minutes=1) point2 = point + timedelta(minutes=1, seconds=1)
with patch( with patch(
"homeassistant.components.recorder.core.dt_util.utcnow", return_value=start "homeassistant.components.recorder.core.dt_util.utcnow", return_value=start