--- obspy/clients/filesystem/db.py
+++ obspy/clients/filesystem/db.py
@@ -11,22 +11,9 @@
 import datetime
 
 from sqlalchemy import Column, String, Integer, Float
-from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy.orm import declarative_base
 from sqlalchemy.schema import PrimaryKeyConstraint
 
-
-# we pinned our dependency to sqlalchemy <2.0 for now, so these warnings are
-# irrelevant. eventually code should be migrated to work with sqlalchemy >=2.0
-# as well
-try:
-    from sqlalchemy.exc import MovedIn20Warning
-except ImportError:
-    pass
-else:
-    import warnings
-    warnings.filterwarnings('ignore', category=MovedIn20Warning)
-
-
 Base = declarative_base()
 
 # Every declarative class should only be instantiated once. Thus we just use a
--- obspy/clients/filesystem/tsindex.py
+++ obspy/clients/filesystem/tsindex.py
@@ -1544,18 +1544,22 @@ def _fetch_index_rows(self, query_rows=None, bulk_params=None):
         # Create a CTE that contains the request
         try:
             stmts = [
-                sa.select([
+                sa.select(
                     sa.literal(a).label("network"),
                     sa.literal(b).label("station"),
                     sa.literal(c).label("location"),
                     sa.literal(d).label("channel"),
-                    sa.literal(e).label("starttime")
-                    if e != '*' else
-                    sa.literal('0000-00-00T00:00:00').label("starttime"),
-                    sa.literal(f).label("endtime")
-                    if f != '*' else
-                    sa.literal('5000-00-00T00:00:00').label("endtime")
-                ])
+                    sa.case(
+                        (sa.literal(e) == '*',
+                            sa.literal('0000-00-00T00:00:00')),
+                        (sa.literal(e) != '*', sa.literal(e))
+                        ).label("starttime"),
+                    sa.case(
+                        (sa.literal(f) == '*',
+                            sa.literal('5000-00-00T00:00:00')),
+                        (sa.literal(f) != '*', sa.literal(f))
+                        ).label("endtime")
+                )
                 for idx, (a, b, c, d, e, f) in enumerate(query_rows)
             ]
             requests = sa.union_all(*stmts)
@@ -1724,18 +1728,22 @@ def _fetch_summary_rows(self, query_rows):
         try:
             request_cte_name = "request_cte"
             stmts = [
-                sa.select([
+                sa.select(
                     sa.literal(a).label("network"),
                     sa.literal(b).label("station"),
                     sa.literal(c).label("location"),
                     sa.literal(d).label("channel"),
-                    sa.literal(e).label("starttime")
-                    if e != '*' else
-                    sa.literal('0000-00-00T00:00:00').label("starttime"),
-                    sa.literal(f).label("endtime")
-                    if f != '*' else
-                    sa.literal('5000-00-00T00:00:00').label("endtime")
-                ])
+                    sa.case(
+                        (sa.literal(e) == '*',
+                            sa.literal('0000-00-00T00:00:00')),
+                        (sa.literal(e) != '*', sa.literal(e))
+                        ).label("starttime"),
+                    sa.case(
+                        (sa.literal(f) == '*',
+                            sa.literal('5000-00-00T00:00:00')),
+                        (sa.literal(f) != '*', sa.literal(f))
+                        ).label("endtime")
+                    )
                 for idx, (a, b, c, d, e, f) in enumerate(query_rows)
             ]
             requests = sa.union_all(*stmts)
--- obspy/pytest.ini
+++ obspy/pytest.ini
@@ -22,14 +22,6 @@ filterwarnings =
     ignore:Auto-removal of grids
 # see issue 3164, can be removed when NRL online tests get removed
     ignore:(?s).*Direct access to online NRL
-# sqlalchemy showing deprecation warnings, seems like big API change with version 2
-# fixed sqlalchemy<2 for now in setup.py
-# also suppressing these warnings directly in clients.filesystem.db because
-# they get raised before test execution during test discovery
-# see https://docs.sqlalchemy.org/en/20/errors.html#error-b8d9
-# see https://docs.sqlalchemy.org/en/20/changelog/migration_20.html
-    ignore:Deprecated API features .* are not compatible with SQLAlchemy 2.0
-    ignore:function is now available as sqlalchemy.orm.declarative_base
 # ignore DeprecationWarnings and PendingDeprecationWarnings triggered by other modules
     ignore::DeprecationWarning:(?!obspy).*
     ignore::PendingDeprecationWarning:(?!obspy).*
--- setup.py
+++ setup.py
@@ -92,7 +92,7 @@
     'matplotlib>=3.3',
     'lxml',
     'setuptools',
-    'sqlalchemy<2',
+    'sqlalchemy>=1.4',
     'decorator',
     'requests',
 ]