AI Moved Into the Database. I've Spent 25 Years Getting Logic Out of It.
written by Stefan Christoph
- 8 minutes readTwenty-five years of pulling logic out
I have spent most of a 25-year career taking logic out of databases. Ripping business rules out of stored procedures. Untangling the trigger that fired the trigger that updated the row that fired the trigger. Explaining, patiently, why the thing that made a query 40% faster also made the system impossible to test, version, reason about, or move.
So when I read that SQL Server 2025 can now call Amazon Bedrock directly from T-SQL, my first reaction was not “how exciting.” It was the specific, weary recognition of an old friend in new clothes. We are about to relearn a lesson, at scale, with a model attached.
Let me be fair to the launch, and then tell you where I think it goes wrong.
What actually shipped (verified)
Checked against the docs, because “AI in the database” is exactly the phrase that invites hand-waving. SQL Server 2025, available on Amazon RDS [1], adds:
sp_invoke_external_rest_endpoint: a system stored procedure that lets T-SQL call an external REST endpoint directly from the engine, “without additional middleware.” AWS documents it calling Amazon Bedrock and AWS Lambda.[2]- A native
vectordata type with vector functions (VECTOR_DISTANCE,VECTOR_NORM,VECTOR_NORMALIZE), verified on RDS for SQL Server 2025.[2] Approximate-nearest-neighbour (DiskANN) indexing is a SQL Server 2025 engine capability per Microsoft [4], confirm its exact status, edition, and limits on RDS against the current docs before relying on it. - RDS wraps this as a managed SQL Server 2025 and handles the AWS-service wiring for the Bedrock/Lambda calls, though the usual IAM permissions, endpoint, and networking configuration still apply.[3]
To be precise: the model still runs in Bedrock, SQL Server is initiating a remote call, not running inference locally. “AI in the database” means the invocation moved into the data tier, not the computation. The feature is Microsoft’s; RDS is the managed path to it on AWS. None of that is in dispute. The question is what you do with it.
Split the launch in two
The mistake in most of the coverage is treating this as one feature. It’s two, and they land on opposite sides of the line I’ve defended for two decades.
| What shipped | My verdict |
|---|---|
Native vector type + VECTOR_DISTANCE, storing embeddings next to the row | Not the anti-pattern. This is data in the data tier, the same category as a full-text index or a spatial type. Legitimate, and genuinely useful: your embeddings live with the source row, retrieval doesn’t need a second datastore. Worth evaluating against your workload’s recall, build-cost, and embedding-freshness needs. |
The database calling Bedrock via sp_invoke_external_rest_endpoint | The anti-pattern reborn. That’s behavior, orchestration, and outbound network I/O in the data tier, precisely what I’ve spent 25 years pulling out. |
If you take one thing from this post: those two halves deserve opposite reactions. Storing a vector is not “logic in the database.” A stored procedure that reaches out to a language model absolutely is.
Why the database calling a model makes it worse
The classic case against logic in the database still holds, unchanged by AI. These are risks, not certainties, how hard each one bites depends on your workload, but they are the same risks they have always been: poor testability, logic hidden from the application it serves, tight coupling to the engine and vendor, and making what is, in most relational deployments, your most expensive and least elastic tier wait on and coordinate work that belongs elsewhere. But putting a model call in there doesn’t just re-commit the old sin. It adds new ones:
- Latency inside a transaction. If you call Bedrock via
sp_invoke_external_rest_endpointinside an open transaction that already holds locks, the synchronous wait, the procedure blocks until the response arrives or its timeout expires (default 30 seconds) [6], extends how long the transaction stays open, and with it how long every lock that transaction holds is retained, putting a slow, variable network round-trip in the critical path of your data tier. (Invoke it outside the transaction boundary and you avoid the lock-extension, though not the latency.) - “No middleware” removes your application-layer boundary. IAM authorization, service quotas, Bedrock Guardrails, and CloudTrail logging don’t disappear, those live in AWS. What you lose is the application layer where retries, request correlation, cost attribution, and your own policy checks naturally sit. Move the call into the engine and you haven’t removed that need, you’ve removed the obvious home for it.
- Non-determinism leaking into the tier that was supposed to be deterministic. The engine itself doesn’t become probabilistic, but database logic that stores or branches on the model’s output now depends on nondeterministic, model-version-sensitive results. The tier whose job was to be boring now carries that variability.
- Cost attribution gets harder. CloudTrail captures the API call events, Bedrock’s model-invocation logging, which you have to enable, captures the invocations themselves, and cost-allocation tags slice the bill. Three mechanisms, three different notions of who the caller is. All of them answer “which application drove that inference?” more easily when a named service made the call than when the database made it on behalf of many callers. Check what caller signal the RDS integration actually surfaces in your logs and billing.
None of these are reasons the feature shouldn’t exist. They’re reasons it shouldn’t be your default.
The honest exception
I’d be a zealot if I said “never,” so I won’t. There are narrow cases where a database-initiated model call is the least-bad option:
- A batch enrichment or classification job where in-transaction latency is irrelevant and an external worker or queue consumer (select rows, call the model, write results back) would genuinely add more moving parts than it’s worth.
- A team with no application tier at all, a reporting or analytics estate where the database is the system.
Even then, treat it as a deliberate exception, documented as such, and put the application-layer controls, retries, request correlation, cost attribution, your own policy checks, somewhere deliberate rather than nowhere. The moment it becomes “just call Bedrock from the proc, it’s easier,” you’ve started down the road whose end I’ve been cleaning up for 25 years.
If you’re running this on AWS
RDS for SQL Server 2025 is, in my view, a strong managed option, and the vector support is a real reason to consider it. So the pragmatic split:
- Do evaluate the native
vectortype for storing and searching embeddings next to your relational data, that’s data-tier work, done well, against your recall, index-build-cost, and embedding-freshness needs (and confirm DiskANN indexing’s current status on RDS before you count on it). A Developer edition lets you try the features at no license cost, though the RDS instance and any Bedrock inference still bill as usual.[2] - Keep the model invocation in your application or a Lambda, where retries, request correlation, cost attribution, and your own policy checks have a home, and where you can complete the database transaction before calling the model rather than holding it open across the round-trip. That split has its own tradeoff, the model call now sits outside the transaction, so a failure between commit and call, or between call and result-write, leaves a gap; when the result must land durably, an outbox-style record plus an idempotent worker that writes the result in its own transaction is the pattern that covers it. RDS makes the Bedrock call possible from T-SQL; that’s not the same as it being the right place for it.[3]
The industry keeps trying to move the AI call closer to the data. Sometimes (vectors) that’s exactly right, because it is data. Sometimes (orchestration) it’s a 25-year-old mistake with a language model bolted on. It’s the same “the boundary moved” question I’ve chased before [5]: the hard part isn’t the capability, it’s noticing which side of the line each half belongs on.
So convince me I’m wrong: what’s the workload where you’d genuinely rather your database called the model than your application did?
Sources
- [1] Amazon RDS for SQL Server now supports Microsoft SQL Server 2025, AWS What’s New, the announcement.
- [2] Microsoft SQL Server features on Amazon RDS, SQL Server 2025 features,
sp_invoke_external_rest_endpoint, native vector type + functions, editions/capacity. - [3] Invoke AWS services directly from Amazon RDS for SQL Server 2025, AWS Database Blog, the managed AWS-service integration path.
- [4] SQL Server 2025 DiskANN improvements for vector search, Microsoft, vector indexing integrated into the engine.
- [5] Understanding Is the New Bottleneck, my prior work on the “the boundary moved” lens.
- [6] sp_invoke_external_rest_endpoint (Transact-SQL), Microsoft Learn, invocation semantics: the procedure waits on the HTTPS call (
HTTP_EXTERNAL_CONNECTIONwait type) until the response is received or@timeoutexpires (default 30 s, max 230 s).
About the Author
Stefan Christoph is a Principal Solutions Architect at AWS, focused on agentic AI, media & entertainment, and helping builders move from demo to production. He writes about AI architecture, developer productivity, and the future of software.
This is a personal blog. Opinions expressed here are my own and do not represent the views or positions of my employer.
🎬 Also available as a blog walkthrough video on YouTube
❤️ Created with the support of AI (Kiro)
📝 Last updated: August 17, 2026 — Technical corrections from a quality audit; Editorial polish for readability and voice