2

I am writing SQL to report data similar to the below format. The row's beginning with 'P' are summary rows for a specific vendor for the corresponding remittance 'R' rows below it associated to the payment or that vendor. For example the PAID_AMT amount on the P rows is a summed value (1000.50 = sum of corresponding R rows 110 + 800 + 100.50 = 1000.50) for 'NW Forest Supplies'. I started doing this as a UNION query however the challenge is that the 'P' rows represent different fields from what the 'R' rows represent, so combining the data together is what I am struggling with.

The final row beginning with 'C' is a overall summary of all rows and I am also struggling with and is not correct. Per the example, it should sum the total PAID_AMT values from the 'P' rows which would be 2056.00 (1000.50 + 55 + 1000.50), then output the total count of the number of payments (3), and then final the total count of remittances - 7 in this example.

Perhaps it's better to make use of a CTE and union in the final result, or make use of temp tables to insert the data for the 'P' and 'R' data into their own tables, and then query from?

Example of how report output needs to be: enter image description here

SELECT
    'P'
    , 'CRD'
    , 'PayerName.Payables'
    , A.ADDRESS4
    , A.PYMNT_ID_REF
    , SUM(B.PAID_AMT)
    , A.REMIT_VENDOR
    , CAST(B.REMIT_ADDR_SEQ_NUM AS VARCHAR)
    , A.NAME1
    , 'USD'
FROM
    PS_PAYMENT_TBL                A
    INNER JOIN PS_PYMNT_VCHR_XREF B
        ON B.PYMNT_ID = A.PYMNT_ID
           AND A.BANK_SETID = B.BANK_SETID
           AND A.BANK_CD = B.BANK_CD
           AND A.BANK_ACCT_KEY = B.BANK_ACCT_KEY
WHERE A.PYMNT_DT > '01/12/2021'
GROUP BY
    A.REMIT_VENDOR
    , A.ADDRESS4
    , A.PYMNT_ID_REF
    , A.REMIT_VENDOR
    , CAST(B.REMIT_ADDR_SEQ_NUM AS VARCHAR)
    , A.NAME1
UNION
SELECT
    'R'
    , A.PYMNT_ID_REF
    , C.INVOICE_ID
    , C.INVOICE_DT
    , C.GROSS_AMT
    , C.DSCNT_AMT
    , B.VOUCHER_ID
    , C.PO_ID
    , ''
    , ''
FROM
    PS_PYMNT_VCHR_XREF        B
    INNER JOIN PS_PAYMENT_TBL A
        ON B.PYMNT_ID = A.PYMNT_ID
           AND A.BANK_SETID = B.BANK_SETID
           AND A.BANK_CD = B.BANK_CD
           AND A.BANK_ACCT_KEY = B.BANK_ACCT_KEY
    INNER JOIN PS_VOUCHER     C
        ON B.VOUCHER_ID = C.VOUCHER_ID
           AND C.BUSINESS_UNIT = B.BUSINESS_UNIT
WHERE A.PYMNT_DT > '01/12/2021'
UNION
SELECT
    'C'
    , SUM(A.PAID_AMT)
    , COUNT(A.PAID_AMT) , COUNT(??)
FROM
    PS_PYMNT_VCHR_XREF        A
    INNER JOIN PS_PAYMENT_TBL B
        ON B.PYMNT_ID = A.PYMNT_ID
           AND A.BANK_SETID = B.BANK_SETID
           AND A.BANK_CD = B.BANK_CD
           AND A.BANK_ACCT_KEY = B.BANK_ACCT_KEY
WHERE A.PYMNT_DT > '01/12/2021'
GROUP BY B.NAME1
ORDER BY 7, 1

Edit 1/17/21:

I modified the SQL slightly, and believe I have the individual parts of the code working, but I am struggling as to how I can combine these the same way the example has it illustrated, with having the Remittances displayed below each Payment.

The UNION was the only function I could think of to combine the data vertically, but I need to be able to group the remittances below each corresponding payment row and I don't know what ways I can do this. Also it's worth mentioning that the columns for the Payment and Remittances queries are different (number of columns and type) and do not always relate to each other.

--Payment:
SELECT B.ADDRESS4, B.PYMNT_ID_REF, SUM(A.PAID_AMT), B.REMIT_VENDOR, A.REMIT_ADDR_SEQ_NUM,
B.NAME1
FROM PS_PYMNT_VCHR_XREF A
INNER JOIN PS_PAYMENT_TBL B ON B.PYMNT_ID = A.PYMNT_ID AND A.BANK_SETID = B.BANK_SETID
     AND A.BANK_CD = B.BANK_CD
     AND A.BANK_ACCT_KEY = B.BANK_ACCT_KEY
WHERE B.PYMNT_DT > '2021-01-03'
--AND A.PYMNT_ID = '0000263556'
GROUP BY B.ADDRESS4, B.PYMNT_ID_REF, B.REMIT_VENDOR, A.REMIT_ADDR_SEQ_NUM,
B.NAME1

--Remittances:
SELECT 2, A.PYMNT_ID_REF, C.INVOICE_ID, C.INVOICE_DT, C.GROSS_AMT, C.DSCNT_AMT, B.VOUCHER_ID,
C.PO_ID
FROM PS_PYMNT_VCHR_XREF B
INNER JOIN PS_PAYMENT_TBL A ON B.PYMNT_ID = A.PYMNT_ID AND A.BANK_SETID = B.BANK_SETID
     AND A.BANK_CD = B.BANK_CD
     AND A.BANK_ACCT_KEY = B.BANK_ACCT_KEY
INNER JOIN PS_VOUCHER C ON B.VOUCHER_ID = C.VOUCHER_ID AND C.BUSINESS_UNIT = B.BUSINESS_UNIT
WHERE A.PYMNT_DT > '2021-01-03'
--AND A.PYMNT_ID = '0000263556'


--Control data:
WITH CONTROLTOTALS AS (
SELECT  SUM(A.PAID_AMT) AS TOT_PAID_AMT
FROM PS_PYMNT_VCHR_XREF A
INNER JOIN PS_PAYMENT_TBL B ON B.PYMNT_ID = A.PYMNT_ID AND A.BANK_SETID = B.BANK_SETID
     AND A.BANK_CD = B.BANK_CD
     AND A.BANK_ACCT_KEY = B.BANK_ACCT_KEY
WHERE B.PYMNT_DT > '2021-01-03' )

, CONTROLCOUNT1 AS (

SELECT COUNT(PYMNT_ID_REF) AS PYMNT_COUNT
FROM PS_PAYMENT_TBL B
WHERE B.PYMNT_DT > '2021-01-03'
AND PYMNT_ID <> '' ) ,

CONTROLCOUNT2 AS (
SELECT COUNT(*) AS REMIT_COUNT
FROM PS_PYMNT_VCHR_XREF A
INNER JOIN PS_PAYMENT_TBL B ON B.PYMNT_ID = A.PYMNT_ID AND A.BANK_SETID = B.BANK_SETID
     AND A.BANK_CD = B.BANK_CD
     AND A.BANK_ACCT_KEY = B.BANK_ACCT_KEY
WHERE PYMNT_DT > '2021-01-03'  )

SELECT 'C', A.TOT_PAID_AMT, B.PYMNT_COUNT, C.REMIT_COUNT
FROM CONTROLTOTALS A, CONTROLCOUNT1 B, CONTROLCOUNT2 C

EDIT 1/18/21:

Here is the table structure for the 3 tables being used in these queries:

enter image description here

Here is some sample data (2 payment rows for two vendors (Radiometer America - payment ID 023946, and Highmark Inc. - Payment ID 023943), along with corresponding remittances rows associated with each vendor:

enter image description here

Here is sample Remittance data that is associate to each payment for a vendor (Highmark is highlighted in Red with 2 Remittances, Radiometer America is in Green with 6 remittances, for a total of 8 for the example output): enter image description here

Here is the final Control (Grand) totals row at the bottom. The TOT_PAID_AMT represents the total 2 payments from above for each vendor (8901.94 + 997,343.44), the total payment count (2), and the total remittances in the report (2 from Highmark, 6 from Radiometer America:

enter image description here

So now I need to bring all this together and output it the same way the original example displays it as, with the remittances displayed below each vendor payment.

This is how the output would look like for this particular example, notice the remittances are displayed with the corresponding payment: enter image description here

So I have all the individual pieces, but I now need to tie them together in this format, for which I am struggling with. Hope this provides more clarity.

Table structure / sample files here:

https://pastebin.com/Ym9PuKXf

https://pastebin.com/F62467zE

https://pastebin.com/4EBrdyjN

https://pastebin.com/iU20L8Jf

https://pastebin.com/k9P75DAJ

https://pastebin.com/f4Ci6qXd

Nick
  • 268
  • 8
  • 33
  • 1
    [Bad habits to kick : using table aliases like (a, b, c) or (t1, t2, t3)](https://sqlblog.org/2009/10/08/bad-habits-to-kick-using-table-aliases-like-a-b-c-or-t1-t2-t3) – marc_s Jan 15 '21 at 16:00

2 Answers2

1

If I understood you correctly you have three query results with variety of columns and you want to combine those in a single column result separated by Pipe("|") delimiter. To achieve that I have add an additional column in all three of your query output name "ConcatedValue" in which i have combined values from all the columns into one. Then to have the value from controls at the end i have introduced a column named sortid, and to maintain order of payment and remittance for each pymnt_id_ref i have introduced a column named internalsortid. Since controls table doesn't have pymnt_id_ref column I have added that with null value.

I have created three tables named payment, remittance and control with your results and get your desired result with below query:

with cte as(
SELECT 1 SorTID,1 internalsortid, pymnt_id_ref,('P|CRD|Hosp.Payables'+'|'+ ADDRESS4+'|'+ PYMNT_ID_REF+'|'+ colc+'|'+REMIT_VENDOR+'|'+ REMIT_ADDR_SEQ_NUM+'|'+NAME1+'|USD')concatedvalue from payment b
union all
SELECT 1 SortID,2 internalsortid, pymnt_id_ref,('R|'+PYMNT_ID_REF +'|'+  INVOICE_ID +'|'+  CONVERT(VARCHAR(10),INVOICE_DT ,120)+'|'+  GROSS_AMT +'|'+  DSCNT_AMT +'|'+  VOUCHER_ID +'|'+  PO_ID )
from Remittance
union all
SELECT 3 SortID,3 internalsortid, NULL pymnt_id_ref, ('C|'+ TOT_PAID_AMT+'|'+ PYMNT_COUNT+'|'+REMIT_COUNT)concatedvalue from control)
select  concatedvalue from cte order by sortid,pymnt_id_ref,internalsortid

Desired Result:

enter image description here

Since I don't have your actual tables I couldn't be able to executer your query after changing it. I am sharing it here. But you if you have any column with data type int, float etc. you need to convert while combining those in a single column. For example if pymnt_id_ref is a int column you need to use below line for payment table's concatedvalue column:

Revised Query:

 With Payment as(
SELECT 'P', 'CRD', 'Hosp.Payables', B.ADDRESS4, B.PYMNT_ID_REF, SUM(A.PAID_AMT), B.REMIT_VENDOR, A.REMIT_ADDR_SEQ_NUM,
B.NAME1, 'USD',1 SorTID,1 internalsortid, 
('P|CRD|Hosp.Payables'+'|'+ b.ADDRESS4+'|'+ b.PYMNT_ID_REF+'|'+ SUM(A.PAID_AMT)+'|'+b.REMIT_VENDOR+'|'+ a.REMIT_ADDR_SEQ_NUM+'|'+b.NAME1+'|USD')concatedvalue 

FROM PS_PYMNT_VCHR_XREF A
INNER JOIN PS_PAYMENT_TBL B ON B.PYMNT_ID = A.PYMNT_ID AND A.BANK_SETID = B.BANK_SETID
     AND A.BANK_CD = B.BANK_CD
     AND A.BANK_ACCT_KEY = B.BANK_ACCT_KEY
WHERE B.PYMNT_DT = '2021-01-04'
 --AND A.PYMNT_ID = '0000263556'
   AND B.REMIT_VENDOR IN ('51503A', '71520A')
   --AND B.REMIT_VENDOR = '71520A'
  AND B.PYMNT_STATUS NOT IN ('S','V')
     AND (( A.BANK_SETID = 'SHARE'
     AND A.BANK_CD = 'MT'
     AND A.BANK_ACCT_KEY = 'TGC')
     OR ( A.BANK_SETID = 'SHARE'
     AND A.BANK_CD = 'PNC'
     AND A.BANK_ACCT_KEY = '1'))
     --AND D.VENDOR_CLASS <> 'E'
GROUP BY B.ADDRESS4, B.PYMNT_ID_REF, B.REMIT_VENDOR, A.REMIT_ADDR_SEQ_NUM,
B.NAME1
--ORDER BY B.NAME1, PYMNT_ID_REF
) ,
Remittance as(
 
--Remittances:
SELECT 2, A.PYMNT_ID_REF, C.INVOICE_ID, C.INVOICE_DT, C.GROSS_AMT, C.DSCNT_AMT, B.VOUCHER_ID,C.PO_ID,
1 SortID,2 internalsortid, ('R|'+A.PYMNT_ID_REF +'|'+  C.INVOICE_ID +'|'+   CONVERT(VARCHAR(10),c.INVOICE_DT ,120) +'|'+  C.GROSS_AMT +'|'+  C.DSCNT_AMT +'|'+      VOUCHER_ID +'|'+  PO_ID )

FROM PS_PYMNT_VCHR_XREF B
INNER JOIN PS_PAYMENT_TBL A ON B.PYMNT_ID = A.PYMNT_ID AND A.BANK_SETID = B.BANK_SETID
     AND A.BANK_CD = B.BANK_CD
     AND A.BANK_ACCT_KEY = B.BANK_ACCT_KEY
INNER JOIN PS_VOUCHER C ON B.VOUCHER_ID = C.VOUCHER_ID AND C.BUSINESS_UNIT = B.BUSINESS_UNIT
WHERE A.PYMNT_DT = '2021-01-04'
 --AND A.PYMNT_ID = '0000263556'
 --AND B.REMIT_VENDOR = '51503A'
 AND A.PYMNT_ID_REF IN ('023946', '023943')
 --OR A.PYMNT_ID_REF =  '023943'
 --ORDER BY PYMNT_ID_REF
 ), 
 
--Control data:
CONTROLTOTALS AS (
SELECT  SUM(A.PAID_AMT) AS TOT_PAID_AMT
FROM PS_PYMNT_VCHR_XREF A
INNER JOIN PS_PAYMENT_TBL B ON B.PYMNT_ID = A.PYMNT_ID AND A.BANK_SETID = B.BANK_SETID
     AND A.BANK_CD = B.BANK_CD
     AND A.BANK_ACCT_KEY = B.BANK_ACCT_KEY
WHERE B.PYMNT_DT = '2021-01-04'
   AND B.PYMNT_ID_REF IN ('023946','023943'))    
 
, CONTROLCOUNT1 AS (
 
SELECT COUNT(PYMNT_ID_REF) AS PYMNT_COUNT
FROM PS_PAYMENT_TBL B
WHERE B.PYMNT_DT = '2021-01-04'
AND PYMNT_ID <> ''
  AND  B.PYMNT_ID_REF IN ('023946','023943')) ,
    

CONTROLCOUNT2 AS (
SELECT COUNT(*) AS REMIT_COUNT
FROM PS_PYMNT_VCHR_XREF A
INNER JOIN PS_PAYMENT_TBL B ON B.PYMNT_ID = A.PYMNT_ID AND A.BANK_SETID = B.BANK_SETID
     AND A.BANK_CD = B.BANK_CD
     AND A.BANK_ACCT_KEY = B.BANK_ACCT_KEY
WHERE PYMNT_DT = '2021-01-04'  
 AND B.PYMNT_ID_REF IN ('023946','023943') ),
finalCTE as(
SELECT SortID, internalsortid,pymnt_id_ref,concatedvalue FROM Payment
union all
SELECT SortID, internalsortid,pymnt_id_ref,concatedvalue FROM Remittance
union all
SELECT 3 SortID,3 internalsortid, NULL pymnt_id_ref, ('C|'+ A.TOT_PAID_AMT+'|'+ B.PYMNT_COUNT+'|'+C.REMIT_COUNT)concatedvalue 
FROM CONTROLTOTALS A, CONTROLCOUNT1 B, CONTROLCOUNT2 C)

select  concatedvalue from finalcte order by sortid,pymnt_id_ref,internalsortid
  • I am getting an Error message for the ORDER BY and a few syntax errors: This ORDER BY error is at the bottom of the first two CTE's (Payment and Remittance) - The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP, OFFSET or FOR XML is also specified. – Nick Jan 19 '21 at 18:57
  • Also the CTE expressions i.e. SELECT 'P' needs to have a column name specified, I don't know how that might affect references to the columns in the final select. -- Msg 8155, Level 16, State 2, Line 108 No column name was specified for column 1 of 'Payment'. Msg 8155, Level 16, State 2, Line 108 No column name was specified for column 2 of 'Payment'. Msg 8155, Level 16, State 2, Line 108 No column name was specified for column 3 of 'Payment'. – Nick Jan 19 '21 at 19:35
  • Also I am getting an Invalid Column name: pymnt_id_ref . And after the concatenated string in the Payment CTE, you have "concatedvalue from payment b" I think the "from payment b" is an error as it's erroring on this. Should this be removed? If you like I can edit the post and paste the code as I have edited it... – Nick Jan 19 '21 at 19:48
  • Since I didn't have the tables I anticipated that. Let's try to make some changes. Is the output ok for you? – Kazi Mohammad Ali Nur Romel Jan 19 '21 at 20:22
  • Sorry attached wrong query. Please try now. You will still get some error. Please share here. Or is it possible to share your screen so that I can try to fix the errors directly. – Kazi Mohammad Ali Nur Romel Jan 19 '21 at 20:26
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/227568/discussion-between-kazi-mohammad-ali-nur-and-nick). – Kazi Mohammad Ali Nur Romel Jan 20 '21 at 05:44
0

You can't make a query in SQL Server that would return the data in exactly the format that you want. In SQL Server (and in any other relational DBMS) the schema is static. Schema is the definition of a table, it is a list of columns in a table and their types. A query in SQL Server returns a table with a specific static list of columns and their types.

In SQL Server you can't have a query that returns 4 columns in one row and 7 columns in another row.

In SQL Server you can't have a query that returns money type values in a column in one row and varchar type values in another row in the same column. All values in a given column must have the same type.


If you need to show the user such mixed data you'd better run three separate queries and use some reporting tool that would show the data neatly in a way that you want.


If you insist on putting everything in one single query you need to decide on what values to put in what column.

In your example the P rows have 10 columns, the R rows have 7 columns, the C rows have 4 columns. So, overall, your query will produce (at least) 10 columns. Some of these columns will have NULL values for R and C rows.

If you want to put values of different type in the same column, like dollar amounts (TOT_PAID_AMT) and some text (CRD), then you can convert numbers into text using the built-in Convert or Format functions.

In the end you use UNION ALL (not UNION) to put data from three independent tables / queries together.

Putting together

In your example you didn't give names to all columns in your query results, so I'll put my names here.

The Payment query returns these columns:

QueryType Col1 Col2 ADDRESS4 PYMNT_ID_REF Col3 REMIT_VENDOR EMIT_ADDR_SEQ_NUM NAME1 Col4

The Remittance query returns these columns:

QueryType PYMNT_ID_REF INVOICE_ID INVOICE_DT GROSS_AMT DSCNT_AMT VOUCHER_ID PO_ID

The Control query return these columns:

QueryType TOT_PAID_AMT PYMNT_COUNT REMIT_COUNT

You can put them into temp tables, or wrap them in CTEs. I'll refer to them as TableP, TableR, TableC below.

Now we can write

SELECT
    QueryType
    ,Col1
    ,Col2
    ,ADDRESS4
    ,PYMNT_ID_REF
    ,Col3
    ,REMIT_VENDOR
    ,EMIT_ADDR_SEQ_NUM
    ,NAME1
    ,Col4
FROM TableP

UNION ALL

SELECT
    QueryType
    ,PYMNT_ID_REF
    ,INVOICE_ID
    ,INVOICE_DT
    ,GROSS_AMT
    ,DSCNT_AMT
    ,VOUCHER_ID
    ,PO_ID
    ,NULL
    ,NULL
FROM TableR

UNION ALL

SELECT
    QueryType
    ,TOT_PAID_AMT
    ,PYMNT_COUNT
    ,REMIT_COUNT
    ,NULL
    ,NULL
    ,NULL
    ,NULL
    ,NULL
    ,NULL
FROM TableC

Add necessary conversions to text, such as CONVERT(nvarchar(255), TOT_PAID_AMT). You don't have to put NULL values at the end, you just need to make sure that each SELECT in the UNION ALL returns 10 columns. And that these columns have the same type.

Sorting

Once you get to the point when you see results from all three queries together you can move on to the last bit - sorting them.

You want to put C values last and R values below corresponding P values. By "corresponding" I'm guessing that we can use the PYMNT_ID_REF to match them. So, I'm adding one helper column here:

SELECT
    'P' AS QueryType
    ,Col1
    ,Col2
    ,ADDRESS4
    ,PYMNT_ID_REF
    ,Col3
    ,REMIT_VENDOR
    ,EMIT_ADDR_SEQ_NUM
    ,NAME1
    ,Col4
    ,NULL
    ,0 AS SortOrder
FROM TableP

UNION ALL

SELECT
    'R' AS QueryType
    ,NULL
    ,NULL
    ,NULL
    ,PYMNT_ID_REF
    ,INVOICE_ID
    ,INVOICE_DT
    ,GROSS_AMT
    ,DSCNT_AMT
    ,VOUCHER_ID
    ,PO_ID
    ,0 AS SortOrder
FROM TableR

UNION ALL

SELECT
    'C' AS QueryType
    ,TOT_PAID_AMT
    ,PYMNT_COUNT
    ,REMIT_COUNT
    ,NULL
    ,NULL
    ,NULL
    ,NULL
    ,NULL
    ,NULL
    ,NULL
    ,1 AS SortOrder
FROM TableC

ORDER BY
    SortOrder          -- C values last
    ,PYMNT_ID_REF      -- group by PYMNT_ID_REF
    ,QueryType         -- P before R
;

For this to work I had to move PYMNT_ID_REF values into the same column (the 5th column) and move NULL values around. I ended up with 12 columns altogether.

Vladimir Baranov
  • 31,799
  • 5
  • 53
  • 90