From 6027f3ea6939a399aeef2578fca17efec0e539df Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Sun, 21 May 2023 10:19:16 -0700 Subject: [PATCH] No need to show common values if everything is null Closes #547 --- sqlite_utils/cli.py | 8 +++++--- sqlite_utils/db.py | 24 ++++++++++++++---------- tests/test_analyze_tables.py | 26 +++++++++++++++++++++++++- 3 files changed, 44 insertions(+), 14 deletions(-) diff --git a/sqlite_utils/cli.py b/sqlite_utils/cli.py index 0c91a8f..46af7ae 100644 --- a/sqlite_utils/cli.py +++ b/sqlite_utils/cli.py @@ -2691,9 +2691,11 @@ def _analyze(db, tables, columns, save, common_limit=10, no_most=False, no_least db["_analyze_tables_"].insert( column_details._asdict(), pk=("table", "column"), replace=True ) - most_common_rendered = _render_common( - "\n\n Most common:", column_details.most_common - ) + most_common_rendered = "" + if column_details.num_null != column_details.total_rows: + most_common_rendered = _render_common( + "\n\n Most common:", column_details.most_common + ) least_common_rendered = _render_common( "\n\n Least common:", column_details.least_common ) diff --git a/sqlite_utils/db.py b/sqlite_utils/db.py index 850a3ae..997c2a5 100644 --- a/sqlite_utils/db.py +++ b/sqlite_utils/db.py @@ -3470,18 +3470,22 @@ class Table(Queryable): most_common_results = [(truncate(value), total_rows)] elif num_distinct != total_rows: if most_common: - most_common_results = [ - (truncate(r[0]), r[1]) - for r in db.execute( - "select [{}], count(*) from [{}] group by [{}] order by count(*) desc, [{}] limit {}".format( - column, table, column, column, common_limit - ) - ).fetchall() - ] - most_common_results.sort(key=lambda p: (p[1], p[0]), reverse=True) + # Optimization - if all rows are null, don't run this query + if num_null == total_rows: + most_common_results = [(None, total_rows)] + else: + most_common_results = [ + (truncate(r[0]), r[1]) + for r in db.execute( + "select [{}], count(*) from [{}] group by [{}] order by count(*) desc, [{}] limit {}".format( + column, table, column, column, common_limit + ) + ).fetchall() + ] + most_common_results.sort(key=lambda p: (p[1], p[0]), reverse=True) if least_common: if num_distinct <= common_limit: - # No need to run the query if it will just return the results in revers order + # No need to run the query if it will just return the results in reverse order least_common_results = None else: least_common_results = [ diff --git a/tests/test_analyze_tables.py b/tests/test_analyze_tables.py index a3e4e2f..f4559ff 100644 --- a/tests/test_analyze_tables.py +++ b/tests/test_analyze_tables.py @@ -40,6 +40,7 @@ def big_db_to_analyze_path(tmpdir): to_insert.append( { "category": category, + "all_null": None, } ) db["stuff"].insert_all(to_insert) @@ -233,7 +234,15 @@ def test_analyze_table_save(db_to_analyze_path): def test_analyze_table_save_no_most_no_least_options( no_most, no_least, big_db_to_analyze_path ): - args = ["analyze-tables", big_db_to_analyze_path, "--save", "--common-limit", "2"] + args = [ + "analyze-tables", + big_db_to_analyze_path, + "--save", + "--common-limit", + "2", + "--column", + "category", + ] if no_most: args.append("--no-most") if no_least: @@ -257,3 +266,18 @@ def test_analyze_table_save_no_most_no_least_options( expected["least_common"] = '[["D", 10], ["C", 20]]' assert rows == [expected] + + +def test_analyze_table_column_all_nulls(big_db_to_analyze_path): + result = CliRunner().invoke( + cli.cli, + ["analyze-tables", big_db_to_analyze_path, "stuff", "--column", "all_null"], + ) + assert result.exit_code == 0 + assert result.output == ( + "stuff.all_null: (1/1)\n\n Total rows: 100\n" + " Null rows: 100\n" + " Blank rows: 0\n" + "\n" + " Distinct values: 0\n\n" + )