{"slug": "multiple-result-sets-how-database-migration-service-automates-sql-server-to", "title": "Multiple result sets: How Database Migration Service automates SQL server to PostgreSQL translation", "summary": "Google Cloud's Database Migration Service (DMS) automates the translation of SQL Server stored procedures with multiple result sets to PostgreSQL by converting them into PL/pgSQL functions that return SETOF refcursor, with scalar return values appended as separate cursor datasets. The service analyzes the number of result sets and scalar returns to apply the appropriate conversion logic, as detailed in a Medium blog post by Google Cloud.", "body_md": "In the Medium blog post, **\"****From MARS to SETOF REFCURSOR: Migrating Multi-Result Stored Procedures to PostgreSQL****,\"** we explored the fundamental architectural differences between SQL Server and PostgreSQL regarding multiple result sets. We looked at how SQL Server natively streams multiple tabular streams from a single execution, whereas PostgreSQL requires a more deliberate strategy using explicit cursor manipulation.\n\nIf you’re facing a massive database migration with hundreds of these procedures, manually rewriting them is a non-starter. This is where automated tools come in. In this post, we’ll explore in detail how **Google Cloud’s Database Migration Service**** (DMS)** approaches this exact challenge, the conversion logic it applies under the hood, and how to actually run and test the generated code.\n\nThere are many reasons to migrate your databases to PostgreSQL, including enterprise performance and availability, a thriving developer and user community, and strong AI capabilities. But tricky queries, like those with multiple result sets, can slow down your migration project.\n\nDMS looks at two specific things: **How many result sets does the procedure return?** and **Does it use a scalar ****RETURN**** value?**\n\nThe decision matrix for the translation looks like this:\n\n|\n|\n|\n|\n|\n|\n|\n1 Result Set OR a Scalar Return Value only |\n|\nHandled natively via an INOUT refcursor parameter or standard variable tracking. |\n|\n|\nMultiple Result Sets OR a combination of Result Sets + Scalar Return |\n|\nConverted to a RETURNS SETOF refcursor block. The scalar return value is appended as its own separate cursor dataset. |\n\nSQL Server utilizes a tabular data stream protocol that allows multiple results to be transmitted over a single connection execution path without explicit declarations. PostgreSQL, by contrast, relies on a distinct execution protocol where multiple datasets are managed deterministically via cursors. To bridge this structural difference, DMS automates the translation logic.\n\nConsider a baseline healthcare reporting scenario. We have a master procedure (sp_GetPatientSummary) that orchestrates data retrieval for a patient by conditionally calling two child procedures: one for lab results (sp_GetPatientLabResults) and one for clinical visits (sp_GetPatientDoctorVisits).\n\nDepending on conditional logic and procedural execution paths, a single execution can return up to **four distinct result sets** plus a status integer indicating whether the patient was found.\n\nSQL\n\nTo align with PostgreSQL's execution model, DMS maps the original T-SQL behavior into a structural PL/pgSQL architecture using `SETOF refcursor`\n\nand explicit cursor management.\n\nFor the child procedure sp_getpatientdoctorvisits, which yields exactly one result set, DMS creates a standard PostgreSQL PROCEDURE utilizing an explicit INOUT refcursor parameter to safely pass the pointer back to the caller.\n\nSQL\n\nFor the master routine and the complex lab child routine, an INOUT parameter isn't enough to capture the varying arrays of output. DMS transforms these into **PL/pgSQL Functions** returning a SETOF refcursor.\n\nTake note of how the translated sp_getpatientlabresults builds its cursors sequentially and, at the very end, dynamically opens a distinct cursor explicitly named \"return_value\" to pass the scalar integer back to the execution stack:\n\nSQL\n\nWhen integrating these migrated routines back into your application data access layer, QA and application engineers need to adapt how they process execution results. Instead of reading standard tabular rows sequentially, the calling application or test harness receives an array of cursor references. To handle this programmatically, your application must fetch the data from each returned portal sequentially. DMS isolates the scalar return value by placing it inside its own dedicated, explicitly named \"return_value\" cursor dataset at the very end of the execution stack. Preparing your development teams for this structural mapping ensures that your application logic and validation scripts can accurately parse the multi-layered response arrays without disruption.\n\nTesting these migrated objects in PostgreSQL requires working within explicit transaction blocks. Because PostgreSQL cursors are bound to the transaction lifecycle, accessing the data from the memory portals requires encapsulating the execution and the data retrieval commands within a single `BEGIN ... COMMIT`\n\nblock.\n\nHere is how you execute and fetch the entire complex dataset for Patient 3 inside PostgreSQL:\n\nSQL\n\nThe DMS product employs a sophisticated pre-processing mechanism to understand the expected result set count and the possible existence of a return value. The ultimate goal is to map every SQL Server procedure into one of three distinct categories: no result sets, a single result set, or multiple/dynamic result set counts.\n\nTo achieve this accurately, DMS performs a deep structural analysis:\n\n**Direct Result Sets:** First, the engine scans the procedure's body to count the direct result sets, which are the explicit SELECT statements executed directly within it.\n\n**Dynamic Considerations:** Special consideration must be given when dealing with looped or conditional `SELECT`\n\n/`EXEC`\n\nstatements. Because these constructs inherently mean the number of returned result sets can differ between executions, the count for that procedure is immediately marked as \"dynamic\".\n\n**Building the Call Network:** When the engine encounters references to other stored procedures, it does not yet know how many result sets those child procedures expose. To solve this, DMS builds a comprehensive directed graph to model the entire call hierarchy between procedures.\n\n**DFS Propagation:** Only once this directed network is fully built can the engine run a Depth First Search (DFS) algorithm. This DFS traversal systematically propagates the result set counts—whether fixed integers or dynamic flags—back up the call chain to the top-level procedures.\n\nConsequently, the generated code is accurately modified to support both returning the outer datasets and allowing child routines to be invoked correctly within nested execution stacks. This graph-based approach guarantees accuracy and seamlessly supports highly complex inter-procedural interactions, including both direct and indirect recursions.\n\nGoogle Cloud’s Database Migration Service takes the guesswork out of structural transformations by programmatically applying the FUNCTION vs. PROCEDURE decision tree based on result set counts. While it completely preserves your core business and conditional execution logic, it does change how application connection pools and QA engineers interact with execution results.\n\nUnderstanding this automated architecture ensures you can effectively map out your validation scripts and configure your data access layers for seamless day-two operations in PostgreSQL.\n\nNow that you learned how to convert multiple result sets, it's time to start a PostgreSQL database on Google Cloud. Please let us know how your journey was.\n\nGet started with a Database Migration Service with the Google Cloud $300 free credits. [Start building for free](https://cloud.google.com/database-migration).", "url": "https://wpnews.pro/news/multiple-result-sets-how-database-migration-service-automates-sql-server-to", "canonical_source": "https://cloud.google.com/blog/products/databases/automating-postgres-translations-with-database-migration-service/", "published_at": "2026-08-04 16:00:00+00:00", "updated_at": "2026-08-04 16:30:33.431739+00:00", "lang": "en", "topics": ["developer-tools"], "entities": ["Google Cloud", "Database Migration Service", "SQL Server", "PostgreSQL", "Medium"], "alternates": {"html": "https://wpnews.pro/news/multiple-result-sets-how-database-migration-service-automates-sql-server-to", "markdown": "https://wpnews.pro/news/multiple-result-sets-how-database-migration-service-automates-sql-server-to.md", "text": "https://wpnews.pro/news/multiple-result-sets-how-database-migration-service-automates-sql-server-to.txt", "jsonld": "https://wpnews.pro/news/multiple-result-sets-how-database-migration-service-automates-sql-server-to.jsonld"}}