Embedding a sql case statement into a sql query that is using FOR XML

安稳与你 提交于 2019-12-13 07:43:17

问题


I received some wonderful assistance from a fellow SO user yesterday (see here ) which allowed me to make a great deal of progress towards my goal. Now I'm attempting to establish whether the magical addition that was suggested can be embedded into an existing query that is producing xml output.

The existing query is as follows:

PROCEDURE [dbo].[CreateLandingPurchaseOrderDetails]

-- Add the parameters for the stored procedure here


@startDate DATE,
@endDate DATE


AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    -- Insert statements for procedure here
    SELECT (SELECT
            Contacts.ContactId AS '@ContactId',
            VesselOwner AS '@Owner',
            FORMAT(SUM(LandingDetails.Quantity * LandingDetails.UnitPrice), 'N2') AS '@Owed',
            SocietyMemberships.WeeklyDeductionRate AS '@WeeklyDeductionRate',
            SocietyMemberships.FromMinimumReturn AS '@FromMinimumReturn',
            Deductions.DeductionRate AS '@DeductionRate',


            (SELECT DISTINCT
                ld1.ProductId AS '@ProductId',
                FORMAT(AVG(ld1.UnitPrice), 'N2') AS '@Cost',
                FORMAT(SUM(ld1.Quantity), 'N2') AS '@Quantity'


            FROM LandingDetails ld1
            INNER JOIN dbo.LandingHeaders lh1
                ON ld1.LandingId = lh1.LandingId
            WHERE Posted = 0
            AND lh1.VesselOwner = LandingHeaders.VesselOwner
            GROUP BY ld1.ProductId
            FOR XML PATH ('Products'), TYPE)

        FROM dbo.LandingDetails
        INNER JOIN dbo.LandingHeaders
            ON LandingDetails.LandingId = LandingHeaders.LandingId
        INNER JOIN dbo.Vessels
            ON LandingHeaders.VesselId = Vessels.VesselId
        INNER JOIN dbo.Contacts
            ON Vessels.OwnerId = Contacts.ContactId
        INNER JOIN dbo.SocietyMemberships
            ON Contacts.SocietyId = SocietyMemberships.SocietyId
        INNER JOIN dbo.Deductions
            ON Vessels.DeductionId = Deductions.DeductionId
        WHERE LandingHeaders.Posted = 0
        AND LandingDate1 BETWEEN @startDate AND @endDate
        GROUP BY    ContactId,
                    VesselOwner,
                    SocietyMemberships.WeeklyDeductionRate,
                    SocietyMemberships.FromMinimumReturn,
                    Deductions.DeductionRate
        ORDER BY ContactId

        FOR XML PATH ('Owner'), TYPE)

    FOR XML PATH ('PurchaseOrders'), TYPE

END

This Produces xml output along these lines;

<PurchaseOrders>
  <Owner ContactId="39" Owner="Paul Joy" Owed="1,609.39" WeeklyDeductionRate="10.00" FromMinimumReturn="110.00" DeductionRate="0.0150">
    <Products ProductId="33" Cost="5.00" Quantity="0.40" />
    <Products ProductId="34" Cost="1.80" Quantity="0.90" />
    <Products ProductId="41" Cost="2.30" Quantity="1.30" />

I would like to add one more attribute to the Owner element ( TotalDeductions ). From the previous question that I asked I can see how it's possible for the sql to calculate the information that I was after in order to produce the Total deductions field. However adding that logic to the FOR XML query has proved elusive. I'm loathe to say it can't be done on the grounds that it seems there is little if anything that can't be done in SQL with a little lateral thought.

If I simply cut and paste the case part of the query to my FOR XML query the compiler points out that Owed isn't a valid name. That I understand. However if I simply embed the owner part of the for xml in the following fashion;

WITH cte AS (
 'embed the part of the FOR XML producing the owner element here
)

SELECT 
    ContactId,
    Owed,
    WeeklyDeductionRate,
    FromMinimumReturn,
    DeductionRate,
    CASE 
       WHEN Owed - (Owed * DeductionRate + WeeklyDeductionRate) > FromMinimumReturn 
       THEN Owed * DeductionRate + WeeklyDeductionRate
       ELSE Owed * DeductionRate END
    AS TotalDeductions
FROM cte

Then leaving aside the compile errors for the moment I'd not be producing the xml that I'm after.

Have I finally found something that actually can't be done in SQL, or have I simply missed the obvious 'lateral thought' that I should have had?

Thanks


回答1:


I think inserting the original case expression at the correct place should work. Try this:

PROCEDURE [dbo].[CreateLandingPurchaseOrderDetails]

-- Add the parameters for the stored procedure here
@startDate DATE, @endDate DATE

AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    -- Insert statements for procedure here
    SELECT (
       SELECT
            Contacts.ContactId AS '@ContactId',
            LandingHeaders.VesselOwner AS '@Owner',
            FORMAT(SUM(LandingDetails.Quantity * LandingDetails.UnitPrice), 'N2') AS '@Owed',
            SocietyMemberships.WeeklyDeductionRate AS '@WeeklyDeductionRate',
            SocietyMemberships.FromMinimumReturn AS '@FromMinimumReturn',
            Deductions.DeductionRate AS '@DeductionRate',

          CASE 
             WHEN SUM(LandingDetails.Quantity * LandingDetails.UnitPrice) - (SUM(LandingDetails.Quantity * LandingDetails.UnitPrice) * DeductionRate + WeeklyDeductionRate) > FromMinimumReturn 
             THEN SUM(LandingDetails.Quantity * LandingDetails.UnitPrice) * DeductionRate + WeeklyDeductionRate
             ELSE SUM(LandingDetails.Quantity * LandingDetails.UnitPrice) * DeductionRate 
          END AS '@TotalDeductions',

            (SELECT DISTINCT
                ld1.ProductId AS '@ProductId',
                FORMAT(AVG(ld1.UnitPrice), 'N2') AS '@Cost',
                FORMAT(SUM(ld1.Quantity), 'N2') AS '@Quantity'

            FROM LandingDetails ld1
            INNER JOIN dbo.LandingHeaders lh1
                ON ld1.LandingId = lh1.LandingId
            WHERE Posted = 0
            AND lh1.VesselOwner = LandingHeaders.VesselOwner
            GROUP BY ld1.ProductId
            FOR XML PATH ('Products'), TYPE)

        FROM dbo.LandingDetails
        INNER JOIN dbo.LandingHeaders
            ON LandingDetails.LandingId = LandingHeaders.LandingId
        INNER JOIN dbo.Vessels
            ON LandingHeaders.VesselId = Vessels.VesselId
        INNER JOIN dbo.Contacts
            ON Vessels.OwnerId = Contacts.ContactId
        INNER JOIN dbo.SocietyMemberships
            ON Contacts.SocietyId = SocietyMemberships.SocietyId
        INNER JOIN dbo.Deductions
            ON Vessels.DeductionId = Deductions.DeductionId
        WHERE LandingHeaders.Posted = 0
        AND LandingDate1 BETWEEN @startDate AND @endDate
        GROUP BY    ContactId,
                    LandingHeaders.VesselOwner,
                    SocietyMemberships.WeeklyDeductionRate,
                    SocietyMemberships.FromMinimumReturn,
                    Deductions.DeductionRate
        ORDER BY ContactId

        FOR XML PATH ('Owner'), TYPE)

    FOR XML PATH ('PurchaseOrders'), TYPE

END

The query can probably be improved but without table definitions and some sample data it's a bit difficult to test it.

Check out this SQL Fiddle for a slightly tweaked version that should give the same result.



来源:https://stackoverflow.com/questions/31229474/embedding-a-sql-case-statement-into-a-sql-query-that-is-using-for-xml

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!