From 0a1d5c85ad29f386e4bc23f79666d5acd3dcf436 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 7 Feb 2023 07:50:44 -0600 Subject: [PATCH] Optimize history.get_last_state_changes query (#87554) fixes undefined --- homeassistant/components/recorder/history.py | 48 ++++++++++--------- tests/components/recorder/test_history.py | 2 +- .../recorder/test_history_db_schema_30.py | 2 +- 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/homeassistant/components/recorder/history.py b/homeassistant/components/recorder/history.py index 72ae57dde27..0f3db70f66a 100644 --- a/homeassistant/components/recorder/history.py +++ b/homeassistant/components/recorder/history.py @@ -518,48 +518,52 @@ def state_changes_during_period( 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: stmt, join_attributes = lambda_stmt_and_join_attributes( schema_version, False, include_last_changed=False ) if schema_version >= 31: - stmt += lambda q: q.filter( - (States.last_changed_ts == States.last_updated_ts) - | States.last_changed_ts.is_(None) + stmt += lambda q: q.where( + States.state_id + == ( + 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: - stmt += lambda q: q.filter( - (States.last_changed == States.last_updated) | States.last_changed.is_(None) + stmt += lambda q: q.where( + 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: stmt += lambda q: q.outerjoin( StateAttributes, States.attributes_id == StateAttributes.attributes_id ) - if schema_version >= 31: - stmt += lambda q: q.order_by( - 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) + + stmt += lambda q: q.order_by(States.state_id.desc()) return stmt 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]]: """Return the last number_of_states.""" - start_time = dt_util.utcnow() - entity_id = entity_id.lower() if entity_id is not None else None - entity_ids = [entity_id] if entity_id is not None else None + entity_id_lower = entity_id.lower() + entity_ids = [entity_id_lower] with session_scope(hass=hass) as session: 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)) return cast( @@ -568,7 +572,7 @@ def get_last_state_changes( hass, session, reversed(states), - start_time, + dt_util.utcnow(), entity_ids, include_start_time_state=False, ), diff --git a/tests/components/recorder/test_history.py b/tests/components/recorder/test_history.py index 83d55f978af..670b08f3e21 100644 --- a/tests/components/recorder/test_history.py +++ b/tests/components/recorder/test_history.py @@ -330,7 +330,7 @@ def test_get_last_state_changes(hass_recorder): start = dt_util.utcnow() - timedelta(minutes=2) point = start + timedelta(minutes=1) - point2 = point + timedelta(minutes=1) + point2 = point + timedelta(minutes=1, seconds=1) with patch( "homeassistant.components.recorder.core.dt_util.utcnow", return_value=start diff --git a/tests/components/recorder/test_history_db_schema_30.py b/tests/components/recorder/test_history_db_schema_30.py index 4384f284fe8..ee4bb7ab051 100644 --- a/tests/components/recorder/test_history_db_schema_30.py +++ b/tests/components/recorder/test_history_db_schema_30.py @@ -257,7 +257,7 @@ def test_get_last_state_changes(hass_recorder): start = dt_util.utcnow() - timedelta(minutes=2) point = start + timedelta(minutes=1) - point2 = point + timedelta(minutes=1) + point2 = point + timedelta(minutes=1, seconds=1) with patch( "homeassistant.components.recorder.core.dt_util.utcnow", return_value=start