Skip to content

AI-200 · Domain 2: Develop AI solutions by using Azure data management services · 33% of the exam

Task 2.2: Develop AI solutions by using Azure Database for PostgreSQL

Connecting with the SDKs, table design and data types, pgvector and DiskANN indexes, sizing compute, memory and storage for vector workloads, filtered similarity search for RAG, and connection pooling.

Study it

Sample questions

Try each one before opening the answer. Every option is explained, with the Microsoft documentation page that proves it.

Question 1 · choose 1

You store 3,072-dimension embeddings from text-embedding-3-large in a vector(3072) column on Azure Database for PostgreSQL flexible server. Creating an HNSW index fails because of the column's dimensions. You need an approximate nearest neighbor index on the full vectors. What do you do?

  1. ACreate an ivfflat index with lists set to rows / 1000
  2. BCreate a diskann index with product_quantized set to true
  3. CRecreate the HNSW index with m = 32 and ef_construction = 128
  4. DChange the column type to vector without a dimension
Show the answer and why
  • ACreate an ivfflat index with lists set to rows / 1000

    Incorrect

    IVFFlat has the same 2,000-dimension limit for indexing as HNSW.

  • BCreate a diskann index with product_quantized set to true

    Correct

    With product quantization, DiskANN (pg_diskann 0.6 and later) can index vectors of up to 16,000 dimensions.

  • CRecreate the HNSW index with m = 32 and ef_construction = 128

    Incorrect

    Build parameters don't lift the 2,000-dimension limit for HNSW indexes.

  • DChange the column type to vector without a dimension

    Incorrect

    Dropping the declared dimension doesn't give any index type a way to handle these 3,072-dimension vectors.

pgvector stores vectors of any size but can index only up to 2,000 dimensions with IVFFlat or HNSW. On flexible server, DiskANN with product quantization supports high-dimension embeddings and keeps more of the index in memory.

Question 2 · choose 1

A table has an HNSW index created with vector_l2_ops on its embedding column. The application's similarity query orders by embedding <=> the query vector and limits to 10 rows. EXPLAIN shows a sequential scan instead of an index scan. What's the fix?

  1. ARaise hnsw.ef_search for the session
  2. BChange the query to use the <#> operator
  3. CCreate the index with vector_cosine_ops, which matches the <=> operator
  4. DRun ANALYZE on the table so the planner refreshes its statistics
Show the answer and why
  • ARaise hnsw.ef_search for the session

    Incorrect

    ef_search tunes recall for an index that's already used; it doesn't make the planner pick a mismatched index.

  • BChange the query to use the <#> operator

    Incorrect

    <#> is negative inner product, which needs vector_ip_ops, so it wouldn't match an L2 index either.

  • CCreate the index with vector_cosine_ops, which matches the <=> operator

    Correct

    The database considers an index only when its access function matches the distance operator; <=> is cosine distance.

  • DRun ANALYZE on the table so the planner refreshes its statistics

    Incorrect

    Fresh statistics don't fix an operator class that doesn't match the query's operator.

Each distance has its own operator and operator class: <-> with vector_l2_ops, <=> with vector_cosine_ops and <#> with vector_ip_ops. The same rule applies to IVFFlat, HNSW and DiskANN indexes.

Question 3 · choose 1

Hundreds of short-lived Azure Functions instances open their own connections to an Azure Database for PostgreSQL flexible server on the Burstable B2s tier, and the server struggles with connection churn. You want the built-in connection pooler with no extra VMs. What do you do?

  1. AEnable pgbouncer.enabled on the Burstable server and keep connecting on port 5432
  2. BRaise max_connections and keep each function opening its own connection
  3. CSet pgbouncer.pool_mode to statement so each query gets its own connection
  4. DMove to General Purpose, enable pgbouncer.enabled and connect on port 6432
Show the answer and why
  • AEnable pgbouncer.enabled on the Burstable server and keep connecting on port 5432

    Incorrect

    The Burstable tier doesn't support built-in PgBouncer, and the pooler isn't on port 5432 anyway.

  • BRaise max_connections and keep each function opening its own connection

    Incorrect

    PostgreSQL uses a process per connection, so piling up connections makes the resource problem worse.

  • CSet pgbouncer.pool_mode to statement so each query gets its own connection

    Incorrect

    Statement pooling breaks multi-statement transactions and still needs a tier where PgBouncer runs.

  • DMove to General Purpose, enable pgbouncer.enabled and connect on port 6432

    Correct

    Built-in PgBouncer runs only on General Purpose and Memory Optimized servers and listens on port 6432 on the same host name.

PgBouncer is built into flexible server on the General Purpose and Memory Optimized tiers, defaults to transaction pooling and accepts connections on port 6432. Changing to Burstable loses the feature.

Question 4 · choose 1

On a new Azure Database for PostgreSQL flexible server, CREATE EXTENSION pgvector fails. You want vector columns in two databases on the server. What should you do?

  1. AAdd pgvector to shared_preload_libraries, restart the server and run CREATE EXTENSION pgvector
  2. BAllowlist vector in azure.extensions, then CREATE EXTENSION vector in each database
  3. CRun CREATE EXTENSION vector once in the postgres database
  4. DInstall the azure_ai extension, which includes the vector type
Show the answer and why
  • AAdd pgvector to shared_preload_libraries, restart the server and run CREATE EXTENSION pgvector

    Incorrect

    The documented step for this extension is allowlisting through azure.extensions, and its name is vector, not pgvector.

  • BAllowlist vector in azure.extensions, then CREATE EXTENSION vector in each database

    Correct

    The extension must be allowlisted first, its name is vector, and it has to be created in every database that uses it.

  • CRun CREATE EXTENSION vector once in the postgres database

    Incorrect

    An extension is created per database; creating it in postgres doesn't make it available in the other databases.

  • DInstall the azure_ai extension, which includes the vector type

    Incorrect

    azure_ai calls Azure AI services such as embeddings; the vector type comes from the vector extension.

Flexible server only lets you create allowlisted extensions. Allow the extension through azure.extensions, check it with SHOW azure.extensions, then run CREATE EXTENSION vector in each target database.

Question 5 · choose 1

Building a diskann index on a 20-million-row embeddings table takes hours. The server has spare memory and vCores during the maintenance window. Which change speeds up the build?

  1. ARaise l_value_ib on the index to build a higher-quality graph
  2. BSet diskann.iterative_search to off
  3. CRaise maintenance_work_mem and allow parallel workers for the build
  4. DCreate the index before loading the data
Show the answer and why
  • ARaise l_value_ib on the index to build a higher-quality graph

    Incorrect

    l_value_ib is the build-time search list size; a higher value gives a better index but makes the build slower, not faster.

  • BSet diskann.iterative_search to off

    Incorrect

    This is a query-time search setting; it doesn't affect index build time.

  • CRaise maintenance_work_mem and allow parallel workers for the build

    Correct

    CREATE INDEX uses maintenance_work_mem and parallel workers when resources allow, which shortens DiskANN builds.

  • DCreate the index before loading the data

    Incorrect

    Microsoft recommends loading data first; building the index after the load is faster and gives a better layout.

For faster DiskANN builds, give CREATE INDEX more memory through maintenance_work_mem and enough parallel maintenance workers, then scale back after the build. The leader process doesn't take part in parallel builds.

Question 6 · choose 1

A nightly re-embedding job on Azure Database for PostgreSQL flexible server needs far more IOPS for a few hours than the rest of the day. The database is only 300 GiB, and the team doesn't want to pay for terabytes of storage just to get IOPS. Which storage choice fits?

  1. APremium SSD sized at 32 TiB to reach its top IOPS tier
  2. BPremium SSD at 512 GiB, scaling the server to more vCores during the job window
  3. CPremium SSD v2, raising provisioned IOPS only for the job window
  4. DMove the server to the Burstable tier for the job window
Show the answer and why
  • APremium SSD sized at 32 TiB to reach its top IOPS tier

    Incorrect

    Premium SSD ties IOPS to disk size, which is exactly the overprovisioning the team wants to avoid.

  • BPremium SSD at 512 GiB, scaling the server to more vCores during the job window

    Incorrect

    More vCores raise the server's IOPS ceiling, but a 512 GiB Premium SSD disk is still provisioned for about 2,300 IOPS.

  • CPremium SSD v2, raising provisioned IOPS only for the job window

    Correct

    Premium SSD v2 lets you adjust capacity, IOPS and throughput independently at any time, up to 80,000 IOPS from 160 GiB.

  • DMove the server to the Burstable tier for the job window

    Incorrect

    Burstable has the lowest IOPS limits and isn't meant for sustained heavy load.

With Premium SSD, IOPS grow with the disk size you provision. Premium SSD v2 has no fixed sizes and lets you change IOPS and throughput independently of capacity, within the limits of the server's compute size.

Question 7 · choose 1

A Python app on Azure Container Apps connects to Azure Database for PostgreSQL flexible server with psycopg. The server is configured for Microsoft Entra authentication and has a role for the app's managed identity. How does the app authenticate without a stored password?

  1. AIt gets a token for https://management.azure.com/.default and passes it as the password
  2. BIt reads the server admin password from an environment variable set from a Container Apps secret
  3. CIt gets a token with DefaultAzureCredential for https://ossrdbms-aad.database.windows.net/.default and passes it as the password
  4. DIt sets sslmode=disable so that the managed identity is passed through the connection
Show the answer and why
  • AIt gets a token for https://management.azure.com/.default and passes it as the password

    Incorrect

    A management-plane token is for Azure Resource Manager, not for signing in to the database.

  • BIt reads the server admin password from an environment variable set from a Container Apps secret

    Incorrect

    That still stores a password, which the requirement rules out.

  • CIt gets a token with DefaultAzureCredential for https://ossrdbms-aad.database.windows.net/.default and passes it as the password

    Correct

    Flexible server accepts a Microsoft Entra access token in place of a password for an Entra-mapped role.

  • DIt sets sslmode=disable so that the managed identity is passed through the connection

    Incorrect

    sslmode only controls TLS for the connection, and the samples use sslmode=require; it plays no part in passing an identity.

With Microsoft Entra authentication, the client requests an access token for the Azure Database for PostgreSQL resource and uses it as the password. Tokens expire, so long-running apps fetch a fresh one for new connections.

Question 8 · choose 2

You design a table for a RAG pipeline on Azure Database for PostgreSQL flexible server. Each chunk has a 1,536-dimension embedding, and queries filter by tenant and document type before ranking by similarity. Which two design choices support this? (Choose TWO.)

  1. ADeclare the embedding column as vector(1536)
  2. BDeclare the embedding column as vector with no dimension so models can change
  3. CStore tenant ID and document type in their own typed columns next to the embedding
  4. DStore the embedding as a JSON text array
  5. EStore each embedding in a separate table per tenant
Show the answer and why
  • ADeclare the embedding column as vector(1536)

    Correct

    A vector column needs its dimensions declared before it can be indexed.

  • BDeclare the embedding column as vector with no dimension so models can change

    Incorrect

    A vector column without dimensions can't be indexed.

  • CStore tenant ID and document type in their own typed columns next to the embedding

    Correct

    Metadata in regular columns can be filtered in the WHERE clause and indexed, alongside the vector search.

  • DStore the embedding as a JSON text array

    Incorrect

    Text can't use the vector operators or a vector index.

  • EStore each embedding in a separate table per tenant

    Incorrect

    This multiplies tables and indexes; a tenant column with a filter serves the requirement.

Use the vector type with explicit dimensions for embeddings so that an IVFFlat, HNSW or DiskANN index can be built, and keep filterable metadata in ordinary columns so a query can filter and order by distance in one statement.

Question 9 · choose 1

A support assistant on Azure Database for PostgreSQL searches an IVFFlat index that was built after the data load with lists = 500, on a table of under a million embeddings. Queries return in milliseconds, but relevant passages are often missing from the top 10. Session settings are at their defaults. You want better recall while the planner keeps using the index. What do you change?

  1. ADrop the IVFFlat index so that queries run an exact search
  2. BRaise ivfflat.probes from its default of 1 to about 50 (lists / 10)
  3. CSet hnsw.ef_search to 100 for the application's sessions
  4. DSet ivfflat.probes to 500 so that every list is searched
Show the answer and why
  • ADrop the IVFFlat index so that queries run an exact search

    Incorrect

    Without an index the search is exact and recall is perfect, but every query scans the whole table, and the index is no longer used.

  • BRaise ivfflat.probes from its default of 1 to about 50 (lists / 10)

    Correct

    probes sets how many lists a search visits; more lists raise recall at some cost in speed, and lists / 10 is the suggested start for tables up to a million rows.

  • CSet hnsw.ef_search to 100 for the application's sessions

    Incorrect

    ef_search sizes the candidate list of HNSW indexes; an IVFFlat search ignores it.

  • DSet ivfflat.probes to 500 so that every list is searched

    Incorrect

    With probes equal to lists, the search becomes an exact search over every list, and the planner stops using the index.

IVFFlat clusters vectors into lists; a search compares the query with the cluster centers and scans only the closest lists, one by default. Raising ivfflat.probes trades speed for recall, starting at lists / 10 up to a million rows and sqrt(lists) above. Probing every list is equivalent to a brute-force search, so the planner doesn't use the index then.

Question 10 · choose 1

Your embeddings come from an Azure OpenAI model and are normalized to length 1. You want the best query performance from pgvector while ranking by similarity. Which approach do you use?

  1. ARank and index by inner product
  2. BRank and index by cosine distance
  3. CRank and index by Euclidean (L2) distance
  4. DRank by cosine distance without an index, so every query is exact
Show the answer and why
  • ARank and index by inner product

    Correct

    For vectors normalized to length 1, Microsoft suggests inner product for the best performance.

  • BRank and index by cosine distance

    Incorrect

    This ranks normalized vectors the same way, but Microsoft points to inner product as the faster choice for them.

  • CRank and index by Euclidean (L2) distance

    Incorrect

    L2 distance works, but it isn't the recommended choice for normalized embeddings.

  • DRank by cosine distance without an index, so every query is exact

    Incorrect

    Exact search without an index gives perfect recall at the expense of performance, which is what the team wants to avoid.

Microsoft's pgvector performance guidance recommends inner product for embeddings normalized to length 1, such as OpenAI embeddings. pgvector computes it with <#>, which returns the negative inner product.

Question 11 · choose 1

A diskann index with product_quantized set to true serves a 40-million row table. Results are fast, but ranking of the top 10 is slightly off compared with an exact search. How does Microsoft suggest improving accuracy without scanning the table?

  1. ARaise hnsw.ef_search for the session so that more candidates are compared
  2. BRebuild the index with product_quantized set to false so it holds 40 million full vectors in memory
  3. CSend the top 10 to a separate ranking model instead of computing distances
  4. DFetch the top 50 through the index in a subquery, then order those by exact distance and keep 10
Show the answer and why
  • ARaise hnsw.ef_search for the session so that more candidates are compared

    Incorrect

    hnsw.ef_search tunes searches on HNSW indexes; it has no effect on a diskann index.

  • BRebuild the index with product_quantized set to false so it holds 40 million full vectors in memory

    Incorrect

    Product quantization is what keeps a large index efficient; turning it off gives that up rather than reranking.

  • CSend the top 10 to a separate ranking model instead of computing distances

    Incorrect

    The documented technique uses exact vector distances, not a ranking model.

  • DFetch the top 50 through the index in a subquery, then order those by exact distance and keep 10

    Correct

    Reranking a small approximate candidate set with full-precision distances restores accuracy cheaply.

Product quantization compresses vectors so more of the index fits in memory, at some cost to precision. Two-step reranking queries the index for an approximate top 50, then reorders them by their actual distance.

Question 12 · choose 1

You backfill embeddings with one UPDATE that calls azure_openai.create_embeddings for 200,000 rows. Halfway through, Azure OpenAI throttles the requests and the statement fails and rolls back. What's the recommended approach?

  1. ARaise timeout_ms so that each call can wait longer for Azure OpenAI
  2. BSet throw_on_error to false and run the single UPDATE again
  3. CRaise the server's max_connections before running the UPDATE
  4. DUpdate rows in batches with a LIMIT, rerunning until none are left, and allow retries with max_attempts
Show the answer and why
  • ARaise timeout_ms so that each call can wait longer for Azure OpenAI

    Incorrect

    Waiting longer per call doesn't lower the request rate that's being throttled.

  • BSet throw_on_error to false and run the single UPDATE again

    Incorrect

    Rows whose calls fail would be left without useful embeddings, and the request rate stays just as high.

  • CRaise the server's max_connections before running the UPDATE

    Incorrect

    Connection limits on PostgreSQL have nothing to do with throttling by Azure OpenAI.

  • DUpdate rows in batches with a LIMIT, rerunning until none are left, and allow retries with max_attempts

    Correct

    Smaller batches avoid throttling and losing large transactions, and max_attempts retries retryable errors.

The azure_ai extension calls Azure OpenAI from SQL. The tutorials vectorize rows in batches (for example LIMIT 500) and repeat, waiting a minute after a failure; the function's max_attempts and retry_delay_ms arguments control retries.

Practise domain 2 →Practise all domains →