关于Correlated subquery用于From(join)的情况

本文探讨了不同数据库系统中FROM子句使用子查询的支持情况。T-SQL和Sybase提供了较好的支持,而PL/SQL(Oracle)、MySQL则存在限制。文章还提到了子查询的相关执行原理。

这里是定义,但有问题的主要是用于from(join)的时候,实际用下来,T-Sql是支持的(Sybase有条件支持),而P/L sql似乎不支持(据说oracle 10支持,但是个bug),StackOverflow有人讨论,还有这个连接也是。

而MySql,现在属于Oracle,也官方说明不支持。

Subqueries in the FROM clause cannot be correlated subqueries. They are materialized (executed to produce a result set) before evaluating the outer query, so they cannot be evaluated per row of the outer query.

这是什么错误 OR: Exception in ASGI application + Exception Group Traceback (most recent call last): | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/_utils.py", line 76, in collapse_excgroups | yield | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 186, in __call__ | async with anyio.create_task_group() as task_group: | ^^^^^^^^^^^^^^^^^^^^^^^^^ | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/anyio/_backends/_asyncio.py", line 763, in __aexit__ | raise BaseExceptionGroup( | ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception) +-+---------------- 1 ---------------- | Traceback (most recent call last): | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/uvicorn/protocols/http/httptools_impl.py", line 409, in run_asgi | result = await app( # type: ignore[func-returns-value] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/uvicorn/middleware/proxy_headers.py", line 60, in __call__ | return await self.app(scope, receive, send) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/fastapi/applications.py", line 1054, in __call__ | await super().__call__(scope, receive, send) | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/applications.py", line 113, in __call__ | await self.middleware_stack(scope, receive, send) | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/middleware/errors.py", line 187, in __call__ | raise exc | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/middleware/errors.py", line 165, in __call__ | await self.app(scope, receive, _send) | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/middleware/cors.py", line 85, in __call__ | await self.app(scope, receive, send) | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 185, in __call__ | with collapse_excgroups(): | ^^^^^^^^^^^^^^^^^^^^ | File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/contextlib.py", line 158, in __exit__ | self.gen.throw(value) | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/_utils.py", line 82, in collapse_excgroups | raise exc | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 187, in __call__ | response = await self.dispatch_func(request, call_next) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | File "/Users/qxz5mik/platform_work/06.renwu/00.fix_bug/test-automation-platform-backend/src/app/middlewares/request_logger_middleware.py", line 17, in dispatch | response = await call_next(request) | ^^^^^^^^^^^^^^^^^^^^^^^^ | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 163, in call_next | raise app_exc | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 149, in coro | await self.app(scope, receive_or_disconnect, send_no_error) | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/middleware/exceptions.py", line 62, in __call__ | await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send) | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app | raise exc | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app | await app(scope, receive, sender) | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/routing.py", line 715, in __call__ | await self.middleware_stack(scope, receive, send) | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/routing.py", line 735, in app | await route.handle(scope, receive, send) | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/routing.py", line 288, in handle | await self.app(scope, receive, send) | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/routing.py", line 76, in app | await wrap_app_handling_exceptions(app, request)(scope, receive, send) | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app | raise exc | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app | await app(scope, receive, sender) | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/routing.py", line 73, in app | response = await f(request) | ^^^^^^^^^^^^^^^^ | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/fastapi/routing.py", line 301, in app | raw_response = await run_endpoint_function( | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/fastapi/routing.py", line 212, in run_endpoint_function | return await dependant.call(**values) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | File "/Users/qxz5mik/platform_work/06.renwu/00.fix_bug/test-automation-platform-backend/src/app/api/v1/ai.py", line 67, in query_ai_analysis_data_table | ai_analysis_data = await ai.analysis_report_time(db, ai_analysis_data) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | File "/Users/qxz5mik/platform_work/06.renwu/00.fix_bug/test-automation-platform-backend/src/app/services/ai.py", line 362, in analysis_report_time | cursor_num = await db.execute(stmt_num) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/ext/asyncio/session.py", line 461, in execute | result = await greenlet_spawn( | ^^^^^^^^^^^^^^^^^^^^^ | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/util/_concurrency_py3k.py", line 190, in greenlet_spawn | result = context.switch(*args, **kwargs) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/orm/session.py", line 2362, in execute | return self._execute_internal( | ^^^^^^^^^^^^^^^^^^^^^^^ | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/orm/session.py", line 2247, in _execute_internal | result: Result[Any] = compile_state_cls.orm_execute_statement( | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/orm/context.py", line 305, in orm_execute_statement | result = conn.execute( | ^^^^^^^^^^^^^ | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/engine/base.py", line 1418, in execute | return meth( | ^^^^^ | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/elements.py", line 515, in _execute_on_connection | return connection._execute_clauseelement( | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/engine/base.py", line 1632, in _execute_clauseelement | compiled_sql, extracted_params, cache_hit = elem._compile_w_cache( | ^^^^^^^^^^^^^^^^^^^^^^ | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/elements.py", line 703, in _compile_w_cache | compiled_sql = self._compiler( | ^^^^^^^^^^^^^^^ | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/elements.py", line 316, in _compiler | return dialect.statement_compiler(dialect, self, **kw) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/compiler.py", line 1429, in __init__ | Compiled.__init__(self, dialect, statement, **kwargs) | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/compiler.py", line 870, in __init__ | self.string = self.process(self.statement, **compile_kwargs) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/compiler.py", line 915, in process | return obj._compiler_dispatch(self, **kwargs) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/visitors.py", line 141, in _compiler_dispatch | return meth(self, **kw) # type: ignore # noqa: E501 | ^^^^^^^^^^^^^^^^ | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/compiler.py", line 4824, in visit_select | text = self._compose_select_body( | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/compiler.py", line 4973, in _compose_select_body | f._compiler_dispatch( | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/visitors.py", line 141, in _compiler_dispatch | return meth(self, **kw) # type: ignore # noqa: E501 | ^^^^^^^^^^^^^^^^ | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/base.py", line 1607, in visit_join | self.process( | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/compiler.py", line 915, in process | return obj._compiler_dispatch(self, **kwargs) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/visitors.py", line 141, in _compiler_dispatch | return meth(self, **kw) # type: ignore # noqa: E501 | ^^^^^^^^^^^^^^^^ | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/compiler.py", line 4309, in visit_lateral | return "LATERAL %s" % self.visit_alias(lateral_, **kw) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/compiler.py", line 4262, in visit_alias | inner = alias.element._compiler_dispatch( | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/visitors.py", line 141, in _compiler_dispatch | return meth(self, **kw) # type: ignore # noqa: E501 | ^^^^^^^^^^^^^^^^ | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/compiler.py", line 4305, in visit_subquery | return self.visit_alias(subquery, **kw) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/compiler.py", line 4234, in visit_alias | inner = alias.element._compiler_dispatch( | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/visitors.py", line 141, in _compiler_dispatch | return meth(self, **kw) # type: ignore # noqa: E501 | ^^^^^^^^^^^^^^^^ | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/compiler.py", line 4734, in visit_select | froms = self._setup_select_stack( | ^^^^^^^^^^^^^^^^^^^^^^^^^ | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/compiler.py", line 4904, in _setup_select_stack | froms = compile_state._get_display_froms( | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/selectable.py", line 4793, in _get_display_froms | raise exc.InvalidRequestError( | sqlalchemy.exc.InvalidRequestError: Select statement '<sqlalchemy.sql.selectable.Select object at 0x1394546e0>' returned no FROM clauses due to auto-correlation; specify correlate(<tables>) to control correlation manually. +------------------------------------ During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/uvicorn/protocols/http/httptools_impl.py", line 409, in run_asgi result = await app( # type: ignore[func-returns-value] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/uvicorn/middleware/proxy_headers.py", line 60, in __call__ return await self.app(scope, receive, send) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/fastapi/applications.py", line 1054, in __call__ await super().__call__(scope, receive, send) File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/applications.py", line 113, in __call__ await self.middleware_stack(scope, receive, send) File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/middleware/errors.py", line 187, in __call__ raise exc File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/middleware/errors.py", line 165, in __call__ await self.app(scope, receive, _send) File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/middleware/cors.py", line 85, in __call__ await self.app(scope, receive, send) File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 185, in __call__ with collapse_excgroups(): ^^^^^^^^^^^^^^^^^^^^ File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/contextlib.py", line 158, in __exit__ self.gen.throw(value) File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/_utils.py", line 82, in collapse_excgroups raise exc File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 187, in __call__ response = await self.dispatch_func(request, call_next) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/qxz5mik/platform_work/06.renwu/00.fix_bug/test-automation-platform-backend/src/app/middlewares/request_logger_middleware.py", line 17, in dispatch response = await call_next(request) ^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 163, in call_next raise app_exc File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/middleware/base.py", line 149, in coro await self.app(scope, receive_or_disconnect, send_no_error) File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/middleware/exceptions.py", line 62, in __call__ await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send) File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app raise exc File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app await app(scope, receive, sender) File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/routing.py", line 715, in __call__ await self.middleware_stack(scope, receive, send) File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/routing.py", line 735, in app await route.handle(scope, receive, send) File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/routing.py", line 288, in handle await self.app(scope, receive, send) File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/routing.py", line 76, in app await wrap_app_handling_exceptions(app, request)(scope, receive, send) File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app raise exc File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app await app(scope, receive, sender) File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/starlette/routing.py", line 73, in app response = await f(request) ^^^^^^^^^^^^^^^^ File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/fastapi/routing.py", line 301, in app raw_response = await run_endpoint_function( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/fastapi/routing.py", line 212, in run_endpoint_function return await dependant.call(**values) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/qxz5mik/platform_work/06.renwu/00.fix_bug/test-automation-platform-backend/src/app/api/v1/ai.py", line 67, in query_ai_analysis_data_table ai_analysis_data = await ai.analysis_report_time(db, ai_analysis_data) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/qxz5mik/platform_work/06.renwu/00.fix_bug/test-automation-platform-backend/src/app/services/ai.py", line 362, in analysis_report_time cursor_num = await db.execute(stmt_num) ^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/ext/asyncio/session.py", line 461, in execute result = await greenlet_spawn( ^^^^^^^^^^^^^^^^^^^^^ File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/util/_concurrency_py3k.py", line 190, in greenlet_spawn result = context.switch(*args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/orm/session.py", line 2362, in execute return self._execute_internal( ^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/orm/session.py", line 2247, in _execute_internal result: Result[Any] = compile_state_cls.orm_execute_statement( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/orm/context.py", line 305, in orm_execute_statement result = conn.execute( ^^^^^^^^^^^^^ File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/engine/base.py", line 1418, in execute return meth( ^^^^^ File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/elements.py", line 515, in _execute_on_connection return connection._execute_clauseelement( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/engine/base.py", line 1632, in _execute_clauseelement compiled_sql, extracted_params, cache_hit = elem._compile_w_cache( ^^^^^^^^^^^^^^^^^^^^^^ File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/elements.py", line 703, in _compile_w_cache compiled_sql = self._compiler( ^^^^^^^^^^^^^^^ File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/elements.py", line 316, in _compiler return dialect.statement_compiler(dialect, self, **kw) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/compiler.py", line 1429, in __init__ Compiled.__init__(self, dialect, statement, **kwargs) File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/compiler.py", line 870, in __init__ self.string = self.process(self.statement, **compile_kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/compiler.py", line 915, in process return obj._compiler_dispatch(self, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/visitors.py", line 141, in _compiler_dispatch return meth(self, **kw) # type: ignore # noqa: E501 ^^^^^^^^^^^^^^^^ File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/compiler.py", line 4824, in visit_select text = self._compose_select_body( ^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/compiler.py", line 4973, in _compose_select_body f._compiler_dispatch( File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/visitors.py", line 141, in _compiler_dispatch return meth(self, **kw) # type: ignore # noqa: E501 ^^^^^^^^^^^^^^^^ File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/base.py", line 1607, in visit_join self.process( File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/compiler.py", line 915, in process return obj._compiler_dispatch(self, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/visitors.py", line 141, in _compiler_dispatch return meth(self, **kw) # type: ignore # noqa: E501 ^^^^^^^^^^^^^^^^ File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/compiler.py", line 4309, in visit_lateral return "LATERAL %s" % self.visit_alias(lateral_, **kw) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/compiler.py", line 4262, in visit_alias inner = alias.element._compiler_dispatch( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/visitors.py", line 141, in _compiler_dispatch return meth(self, **kw) # type: ignore # noqa: E501 ^^^^^^^^^^^^^^^^ File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/compiler.py", line 4305, in visit_subquery return self.visit_alias(subquery, **kw) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/compiler.py", line 4234, in visit_alias inner = alias.element._compiler_dispatch( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/visitors.py", line 141, in _compiler_dispatch return meth(self, **kw) # type: ignore # noqa: E501 ^^^^^^^^^^^^^^^^ File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/compiler.py", line 4734, in visit_select froms = self._setup_select_stack( ^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/compiler.py", line 4904, in _setup_select_stack froms = compile_state._get_display_froms( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/qxz5mik/platform_work/04.code/test-automation-platform-backend/.venv/lib/python3.12/site-packages/sqlalchemy/sql/selectable.py", line 4793, in _get_display_froms raise exc.InvalidRequestError( sqlalchemy.exc.InvalidRequestError: Select statement '<sqlalchemy.sql.selectable.Select object at 0x1394546e0>' returned no FROM clauses due to auto-correlation; specify correlate(<tables>) to control correlation manually.
08-07
# repositories/user_repo.py from typing import Dict, List, Optional from .base import BaseRepository from models import Department, User from sqlalchemy import select, func from sqlalchemy.ext.asyncio import AsyncSession class UserRepository(BaseRepository[User]): def __init__(self, db: AsyncSession): super().__init__(User, db) async def get_by_account(self, account: str) -> Optional[User]: stmt = select(User).where(User.account == account) result = await self.db.execute(stmt) return result.scalar_one_or_none() async def get_with_department(self, user_id: int) -> Optional[Dict]: stmt = select( User.id, User.account, User.role, User.department_id, User.created_at, func.coalesce(Department.name, "").label("dept_name") ).outerjoin(Department, User.department_id == Department.id)\ .where(User.id == user_id) result = await self.db.execute(stmt) row = result.fetchone() return dict(row._mapping) if row else None async def list_users(self, role: str = None, dept_id: int = None, skip: int = 0, limit: int = 10) -> tuple[int, List[User]]: stmt = select(User) count_stmt = select(func.count()).select_from(User) if role: stmt = stmt.where(User.role == role) count_stmt = count_stmt.where(User.role == role) if dept_id is not None: stmt = stmt.where(User.department_id == dept_id) count_stmt = count_stmt.where(User.department_id == dept_id) total = (await self.db.execute(count_stmt)).scalar() stmt = stmt.offset(skip).limit(limit).order_by(User.created_at.desc()) result = await self.db.execute(stmt) return total, result.scalars().all() # repositories/share_repo.py from typing import Dict, List, Optional from models import Share, User, Character from sqlalchemy import select, func, or_, and_ from sqlalchemy.ext.asyncio import AsyncSession class ShareRepository: def __init__(self, db: AsyncSession): self.db = db async def create_share(self, **data) -> Share: share = Share(**data) self.db.add(share) await self.db.flush() return share async def get_share_with_author(self, share_id: int) -> Optional[Dict]: stmt = select( Share, User.account.label("author_account"), User.department_id, Character.name.label("ai_char_name") ).join(User, Share.author_id == User.id)\ .outerjoin(Character, Share.ai_character_id == Character.id)\ .where(Share.id == share_id) result = await self.db.execute(stmt) row = result.fetchone() if not row: return None data = {k: getattr(row.Share, k) for k in Share.__table__.columns.keys()} data.update({ "author_account": row.author_account, "department_id": row.department_id, "ai_char_name": row.ai_char_name }) return data async def list_shares(self, is_public: bool = None, author_id: int = None, type: str = None, order_by: str = "created_at DESC", skip: int = 0, limit: int = 10) -> tuple[int, List[Dict]]: valid_orders = { "created_at DESC": Share.created_at.desc(), "created_at ASC": Share.created_at.asc(), "like_count DESC": Share.like_count.desc(), "view_count DESC": Share.view_count.desc() } order_clause = valid_orders.get(order_by, Share.created_at.desc()) stmt = select( Share, User.account.label("author_account"), Character.name.label("ai_char_name") ).join(User, Share.author_id == User.id)\ .outerjoin(Character, Share.ai_character_id == Character.id) count_stmt = select(func.count(Share.id)) filters = [] if is_public is not None: filters.append(Share.is_public == is_public) if author_id: filters.append(Share.author_id == author_id) if type: filters.append(Share.type == type) if filters: stmt = stmt.where(and_(*filters)) count_stmt = count_stmt.where(and_(*filters)) total = (await self.db.execute(count_stmt)).scalar() stmt = stmt.order_by(order_clause).offset(skip).limit(limit) result = await self.db.execute(stmt) rows = result.fetchall() data = [] for r in rows: d = {k: getattr(r.Share, k) for k in Share.__table__.columns.keys()} d["author_account"] = r.author_account d["ai_char_name"] = r.ai_char_name data.append(d) return total, data # repositories/room_repo.py from typing import Dict, List, Optional from models import Character, Room, RoomMember, User from sqlalchemy import select, func, exists from sqlalchemy.ext.asyncio import AsyncSession class RoomRepository: def __init__(self, db: AsyncSession): self.db = db async def create_room(self, **data) -> Room: room = Room(**data) self.db.add(room) await self.db.flush() return room async def get_room_with_creator(self, room_id: int) -> Optional[Dict]: stmt = select( Room.id, Room.name, Room.type, Room.description, Room.creator_id, Room.dept_id, Room.ai_character_id, Room.created_at, User.account.label("creator_account"), Character.name.label("ai_char_name"), ).select_from(Room)\ .join(User, Room.creator_id == User.id)\ .outerjoin(Character, Room.ai_character_id == Character.id)\ .where(Room.id == room_id) result = await self.db.execute(stmt) row = result.mappings().fetchone() return dict(row) if row else None async def list_rooms(self, type: str = None, dept_id: int = None, skip: int = 0, limit: int = 10) -> tuple[int, List[Dict]]: subquery = select(RoomMember.room_id).where(RoomMember.user_id == User.id).correlate(Room) stmt = select( Room.id, Room.name, Room.type, Room.description, Room.creator_id, User.account.label("creator_account"), Room.created_at, func.count(RoomMember.user_id).label("member_count") ).join(User, Room.creator_id == User.id)\ .outerjoin(RoomMember, Room.id == RoomMember.room_id) count_stmt = select(func.count(Room.id)) if type: stmt = stmt.where(Room.type == type) count_stmt = count_stmt.where(Room.type == type) if dept_id is not None: stmt = stmt.where(Room.dept_id == dept_id) count_stmt = count_stmt.where(Room.dept_id == dept_id) stmt = stmt.group_by(Room.id).order_by(Room.created_at.desc()).offset(skip).limit(limit) count = (await self.db.execute(count_stmt)).scalar() result = await self.db.execute(stmt) return count, [dict(r._mapping) for r in result.fetchall()] # repositories/base.py from typing import TypeVar, Generic, Optional, List, Dict, Any from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select, update as sql_update, delete as sql_delete from sqlalchemy.orm import DeclarativeBase # 定义类型变量 ModelType = TypeVar("ModelType", bound=DeclarativeBase) # 所有 ORM 模型都继承自 DeclarativeBase class BaseRepository(Generic[ModelType]): def __init__(self, model: type[ModelType], db: AsyncSession): self.model = model self.db = db async def get_by_id(self, obj_id: int) -> Optional[ModelType]: stmt = select(self.model).where(self.model.id == obj_id) result = await self.db.execute(stmt) return result.scalar_one_or_none() async def get_all(self, skip: int = 0, limit: int = 100) -> List[ModelType]: stmt = select(self.model).offset(skip).limit(limit) result = await self.db.execute(stmt) return list(result.scalars().all()) async def create(self, **kwargs) -> ModelType: obj = self.model(**kwargs) self.db.add(obj) await self.db.flush() return obj async def update(self, obj_id: int, **updates) -> bool: stmt = sql_update(self.model).where(self.model.id == obj_id).values(**updates) result = await self.db.execute(stmt) return result.rowcount > 0 async def delete(self, obj_id: int) -> bool: stmt = sql_delete(self.model).where(self.model.id == obj_id) result = await self.db.execute(stmt) return result.rowcount > 0 # database.py import os from dotenv import load_dotenv from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker from typing import AsyncGenerator load_dotenv() DATABASE_URL = os.getenv( "DATABASE_URL", "mysql+asyncmy://root:123456@localhost/ai_roleplay?charset=utf8mb4" ) # 创建异步引擎 engine = create_async_engine( DATABASE_URL, echo=False, pool_pre_ping=True, pool_size=10, max_overflow=20, pool_recycle=3600, ) # 异步 session 工厂 async_session = async_sessionmaker( bind=engine, class_=AsyncSession, expire_on_commit=False, autoflush=False ) # 依赖注入:获取数据库会话 async def get_db() -> AsyncGenerator[AsyncSession, None]: async with async_session() as session: try: yield session await session.commit() except Exception: await session.rollback() raise finally: await session.close() # models.py - 所有数据库表的 ORM 映射 from sqlalchemy import Column, Integer, String, Text, Boolean, DateTime, ForeignKey, func from sqlalchemy.orm import declarative_base, relationship from datetime import datetime, timezone Base = declarative_base() class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True, index=True) account = Column(String(50), unique=True, index=True) password = Column(String(255)) role = Column(String(20), default="user") department_id = Column(Integer, ForeignKey("departments.id"), nullable=True) created_at = Column(DateTime, default=datetime.now(timezone.utc)) # 关系 department = relationship("Department", back_populates="users") rooms_created = relationship("Room", back_populates="creator") room_memberships = relationship("RoomMember", back_populates="user") shares = relationship("Share", back_populates="author") comments = relationship("Comment", back_populates="commenter") search_records = relationship("SearchRecord", back_populates="user") class Department(Base): __tablename__ = "departments" id = Column(Integer, primary_key=True, index=True) name = Column(String(50), unique=True) description = Column(Text) created_at = Column(DateTime, default=datetime.now(timezone.utc)) users = relationship("User", back_populates="department") rooms = relationship("Room", back_populates="department") class Room(Base): __tablename__ = "rooms" id = Column(Integer, primary_key=True, index=True) name = Column(String(100)) type = Column(String(20)) # public, private, department dept_id = Column(Integer, ForeignKey("departments.id"), nullable=True) ai_character_id = Column(Integer, ForeignKey("characters.id"), nullable=True) description = Column(Text) creator_id = Column(Integer, ForeignKey("users.id")) created_at = Column(DateTime, default=datetime.now(timezone.utc)) creator = relationship("User", back_populates="rooms_created") department = relationship("Department", back_populates="rooms") ai_character = relationship("Character") members = relationship("RoomMember", back_populates="room", cascade="all, delete-orphan") messages = relationship("RoomMessage", back_populates="room", cascade="all, delete-orphan") class RoomMember(Base): __tablename__ = "room_members" id = Column(Integer, primary_key=True) room_id = Column(Integer, ForeignKey("rooms.id"), nullable=False) user_id = Column(Integer, ForeignKey("users.id"), nullable=False) joined_at = Column(DateTime, default=datetime.now(timezone.utc)) room = relationship("Room", back_populates="members") user = relationship("User", back_populates="room_memberships") class RoomMessage(Base): __tablename__ = "room_messages" id = Column(Integer, primary_key=True) room_id = Column(Integer, ForeignKey("rooms.id"), nullable=False) sender_id = Column(Integer, ForeignKey("users.id"), nullable=False) content = Column(Text, nullable=False) sent_at = Column(DateTime, default=datetime.now(timezone.utc)) room = relationship("Room", back_populates="messages") sender = relationship("User") class Character(Base): __tablename__ = "characters" id = Column(Integer, primary_key=True, index=True) name = Column(String(50)) trait = Column(Text) avatar_url = Column(String(255)) created_at = Column(DateTime, default=datetime.now(timezone.utc)) shares = relationship("Share", back_populates="ai_character") conversations = relationship("UserConversation", back_populates="character") class Share(Base): __tablename__ = "shares" id = Column(Integer, primary_key=True, index=True) title = Column(String(200)) content = Column(Text) author_id = Column(Integer, ForeignKey("users.id")) is_public = Column(Boolean, default=True) type = Column(String(20)) # public, draft, private ai_character_id = Column(Integer, ForeignKey("characters.id"), nullable=True) view_count = Column(Integer, default=0) like_count = Column(Integer, default=0) comment_count = Column(Integer, default=0) created_at = Column(DateTime, default=datetime.now(timezone.utc)) author = relationship("User", back_populates="shares") ai_character = relationship("Character", back_populates="shares") likes = relationship("ShareLike", back_populates="share", cascade="all, delete-orphan") comments = relationship("Comment", back_populates="share", cascade="all, delete-orphan") class ShareLike(Base): __tablename__ = "share_likes" id = Column(Integer, primary_key=True) share_id = Column(Integer, ForeignKey("shares.id"), nullable=False) user_id = Column(Integer, ForeignKey("users.id"), nullable=False) created_at = Column(DateTime, default=datetime.now(timezone.utc)) share = relationship("Share", back_populates="likes") user = relationship("User") class Comment(Base): __tablename__ = "comments" id = Column(Integer, primary_key=True) share_id = Column(Integer, ForeignKey("shares.id"), nullable=False) commenter_id = Column(Integer, ForeignKey("users.id"), nullable=False) content = Column(Text, nullable=False) parent_id = Column(Integer, ForeignKey("comments.id"), nullable=True) created_at = Column(DateTime, default=datetime.now(timezone.utc)) share = relationship("Share", back_populates="comments") commenter = relationship("User", back_populates="comments") replies = relationship("Comment", backref="parent", remote_side=[id]) class SearchRecord(Base): __tablename__ = "search_records" id = Column(Integer, primary_key=True) keyword = Column(String(100), index=True) user_id = Column(Integer, ForeignKey("users.id"), nullable=True) search_time = Column(DateTime, default=datetime.now(timezone.utc)) user = relationship("User", back_populates="search_records") class UserConversation(Base): __tablename__ = "user_conversations" id = Column(Integer, primary_key=True) user_id = Column(Integer, nullable=False, index=True) character_id = Column(Integer, ForeignKey("characters.id")) user_message = Column(Text, nullable=False) ai_message = Column(Text, nullable=False) timestamp = Column(DateTime, default=datetime.now(timezone.utc)) character = relationship("Character", back_populates="conversations") 最后检查数据库模块内容,看一下结构有没有问题
最新发布
11-08
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值