James Lea
asked on
Crosstab query for QMF/DB2
Hello. I am trying to create a crosstab query in QMF/DB2. I know that there is no Transform function so can this be done in QMF/DB2?
The fields I'm working with are Store#, CSG, and Order#. I would like the row headings to be the Store#, the column headings to be the CSG,
and I want to calculate the count of Order#.
Thanks,
James
The fields I'm working with are Store#, CSG, and Order#. I would like the row headings to be the Store#, the column headings to be the CSG,
and I want to calculate the count of Order#.
Thanks,
James
ASKER
Hi Kent,
It is just one table named MRR. The Store# is a 7 digit alpha numeric field, the CSG is a 3 digit alpha numeric field, and the Order# is a 14 digit alpha numeric field. Is this what you needed?
Thanks,
James
It is just one table named MRR. The Store# is a 7 digit alpha numeric field, the CSG is a 3 digit alpha numeric field, and the Order# is a 14 digit alpha numeric field. Is this what you needed?
Thanks,
James
The traditional query would look something like this:
SELECT StoreNumber, CSG, count(OrderNumber) Count FROM ...
And you'd get results like
StoreNumber CSG Count
AAAAAAA A01 1
AAAAAAA G12 4
BBBBBBB A04 2
To Pivot (Crosstab) this in DB2 is a bit tricky, especially if there are a lot of stores. Can you live with each row being a single string? If so, recursive SQL will do this quite nicely. Otherwise, we'll need to be creative and the query source will grow with the number of stores (columns).
A single string, delimited by tabs, commas, semi-colons, etc. is pretty easy.
SELECT StoreNumber, CSG, count(OrderNumber) Count FROM ...
And you'd get results like
StoreNumber CSG Count
AAAAAAA A01 1
AAAAAAA G12 4
BBBBBBB A04 2
To Pivot (Crosstab) this in DB2 is a bit tricky, especially if there are a lot of stores. Can you live with each row being a single string? If so, recursive SQL will do this quite nicely. Otherwise, we'll need to be creative and the query source will grow with the number of stores (columns).
A single string, delimited by tabs, commas, semi-colons, etc. is pretty easy.
ASKER
Kent,
I would like the end result to look like this:
Store# CSG(1) CSG(2) CSG(3) CSG(4) etc...
szd3279 1000 550 772 124 etc...
uyt0019 623 429 333 44 etc...
There are only up to 6 Store#'s and up to about 30 CSG's.
I would like the end result to look like this:
Store# CSG(1) CSG(2) CSG(3) CSG(4) etc...
szd3279 1000 550 772 124 etc...
uyt0019 623 429 333 44 etc...
There are only up to 6 Store#'s and up to about 30 CSG's.
ASKER
Kent,
I forgot to add a totals column at the end:
Store# CSG(1) CSG(2) CSG(3) CSG(4) etc... Total
szd3279 1000 550 772 124 etc... 2446
uyt0019 623 429 333 44 etc... 1429
There are only up to 6 Store#'s and up to about 30 CSG's.
I forgot to add a totals column at the end:
Store# CSG(1) CSG(2) CSG(3) CSG(4) etc... Total
szd3279 1000 550 772 124 etc... 2446
uyt0019 623 429 333 44 etc... 1429
There are only up to 6 Store#'s and up to about 30 CSG's.
Is that up to 30 CSGs per store (implying up to 180 columns) or 30 combinations?
If this is going to feed into a spreadsheet, I'll suggest the single string per line. If it will populate a grid in your database client we'll need to build out the full SQL, or build meta-SQL. That might actually be easier.
If this is going to feed into a spreadsheet, I'll suggest the single string per line. If it will populate a grid in your database client we'll need to build out the full SQL, or build meta-SQL. That might actually be easier.
ASKER
Kent,
It would be 30 possible combinations, only 30 columns max. I am working in QMF and would be exporting it to a word or excel document. Hoping that the end result would look like the attached file.
QMF-Qry-Ex1.xlsx
It would be 30 possible combinations, only 30 columns max. I am working in QMF and would be exporting it to a word or excel document. Hoping that the end result would look like the attached file.
QMF-Qry-Ex1.xlsx
Hi James,
I've got a query that should do the job. It writes the selected data out in a csv (comma delimited) file that's suitable for importing into Excel. The result of the query with the data described below is:
Store#, C01, C02, C03, C04
AAA000001, 2, 0, 0, 0
BBB000001, 1, 2, 0, 0
CCC000001, 2, 0, 1, 1
DDD000001, 3, 1, 1, 0
Will this work for you?
I tested it by creating the table and data:
I've got a query that should do the job. It writes the selected data out in a csv (comma delimited) file that's suitable for importing into Excel. The result of the query with the data described below is:
Store#, C01, C02, C03, C04
AAA000001, 2, 0, 0, 0
BBB000001, 1, 2, 0, 0
CCC000001, 2, 0, 1, 1
DDD000001, 3, 1, 1, 0
Will this work for you?
WITH g (rn, csg) as
(
SELECT row_number () over (order by csg) rn, csg
FROM (SELECT distinct csg FROM a) t3
),
b (rn, store, csg, count) as
(
SELECT row_number () over (partition by t0.store order by t0.store, t1.csg) RN, t0.store, t1.csg, t2.count
FROM (select distinct store from a) t0 cross join g t1
LEFT JOIN (SELECT store, csg, count(ordernumber) count FROM a group by store, csg) t2
ON t0.store = t2.store
AND t1.csg = t2.csg
),
c (ln, store, text) as
(
SELECT 1, b.store, cast (b.store as varchar (100)) FROM b WHERE rn = 1
UNION ALL
SELECT ln+1, c.store, text || ', ' || cast (coalesce (count,0) as varchar(5))
FROM c, b
WHERE c.store = b.store
AND c.ln = b.rn
),
H (tn, text) as
(
SELECT 1, cast('Store#' as varchar (100)) text from sysibm.sysdummy1
UNION ALL
SELECT tn+1, text || ', ' || csg
FROM g, h
WHERE tn = rn
)
SELECT Text
FROM c
WHERE c.ln = (SELECT count(*) from g) + 1
UNION ALL
SELECT text FROM H
WHERE h.tn = (SELECT count(*) from g) + 1;
I tested it by creating the table and data:
CREATE TABLE a (store varchar(20), csg varchar(10), ordernumber integer)
INSERT INTO a values ('AAA000001', 'C01', 1)
INSERT INTO a values ('AAA000001', 'C01', 2)
INSERT INTO a values ('BBB000001', 'C01', 3)
INSERT INTO a values ('BBB000001', 'C02', 4)
INSERT INTO a values ('BBB000001', 'C02', 5)
INSERT INTO a values ('CCC000001', 'C01', 6)
INSERT INTO a values ('CCC000001', 'C03', 7)
INSERT INTO a values ('CCC000001', 'C01', 8)
INSERT INTO a values ('CCC000001', 'C04', 9)
INSERT INTO a values ('DDD000001', 'C01', 10)
INSERT INTO a values ('DDD000001', 'C01', 11)
INSERT INTO a values ('DDD000001', 'C02', 12)
INSERT INTO a values ('DDD000001', 'C03', 13)
INSERT INTO a values ('DDD000001', 'C01', 14)
COMMIT
ASKER
Kent,
I am getting the following error:
BIC0004. DAL01008. An error occurred while accessing the database. VALUE SZ3279, 5, 1779, 465, 1614, 2777, 160, 110, 3, 338, 2173, 5, 8, 36, 3 IS TOO LONG. SQL CODE=-433, SQLSTATE=2201,DRIVER=4.17. 29
I am getting the following error:
BIC0004. DAL01008. An error occurred while accessing the database. VALUE SZ3279, 5, 1779, 465, 1614, 2777, 160, 110, 3, 338, 2173, 5, 8, 36, 3 IS TOO LONG. SQL CODE=-433, SQLSTATE=2201,DRIVER=4.17.
You said "CSG is a 3 digit alpha numeric field", but you're showing us multiple CSG values that apparently have 4 digits. Please provide actual database column definitions. Thirty 3-digit values account for 90 positions, but thirsty 4-digit values need a larger space, 120 positions. Declaring lengths requires knowing some details.
ASKER
The CSG is a 3 digit alpha numeric such as B15, B16, B17, etc. The values that the error message shows are the total number (count) of the orders for the store# (SZ3279) by each CSG. For example, for SZ3279, CSG B15 had 5 orders, CSG B16 had 1779 orders, CSG B17 had 465 orders, etc. There are no thirsty 4 digit CSG values.
Ah. Those are order counts... so "CSG(1)" doesn't mean "the 1st CSG", and below that the order count is 1000 for that CSG.
That question essentially asks whether all vertical CSG columns will provide order counts for the same CSGs or if they're simple ordinal positions in each row.
Regardless, in the provided test script, try replacing all "varchar (100)" with "varchar (200)" or maybe "varchar (500)". Lengths simply seem a bit short, so make them longer.
For example, for SZ3279, CSG B15 had 5 orders, CSG B16 had 1779 orders, CSG B17 had 465 orders, etc.So would the column headings then be "CSG(B15) CSG(B16) CSG(B17) ... etc."? Or would they remain as "CSG(1) CSG(2) CSG(3) CSG(4) etc."?
That question essentially asks whether all vertical CSG columns will provide order counts for the same CSGs or if they're simple ordinal positions in each row.
Regardless, in the provided test script, try replacing all "varchar (100)" with "varchar (200)" or maybe "varchar (500)". Lengths simply seem a bit short, so make them longer.
Hi James,
One of the oddities of recursive SQL in DB2 is that string (varchar) lengths are determined by the first result. The item can grow during the recursive process, but only up to that limit. The good news is that it's easy to fix be recasting the varchar item in the upper sub-query. Here's the same query with a longer string length for the returned item. I realize that I also didn't include the totals by line column, so I'll work that up and post it shortly.
The only change between the two queries is that the two lines above the UNION ALL statements recast the string to varchar(1000) instead of 100.
One of the oddities of recursive SQL in DB2 is that string (varchar) lengths are determined by the first result. The item can grow during the recursive process, but only up to that limit. The good news is that it's easy to fix be recasting the varchar item in the upper sub-query. Here's the same query with a longer string length for the returned item. I realize that I also didn't include the totals by line column, so I'll work that up and post it shortly.
The only change between the two queries is that the two lines above the UNION ALL statements recast the string to varchar(1000) instead of 100.
WITH g (rn, csg) as
(
SELECT row_number () over (order by csg) rn, csg
FROM (SELECT distinct csg FROM a) t3
),
b (rn, store, csg, count) as
(
SELECT row_number () over (partition by t0.store order by t0.store, t1.csg) RN, t0.store, t1.csg, t2.count
FROM (select distinct store from a) t0 cross join g t1
LEFT JOIN (SELECT store, csg, count(ordernumber) count FROM a group by store, csg) t2
ON t0.store = t2.store
AND t1.csg = t2.csg
),
c (ln, store, text) as
(
SELECT 1, b.store, cast (b.store as varchar (1000)) FROM b WHERE rn = 1
UNION ALL
SELECT ln+1, c.store, text || ', ' || cast (coalesce (count,0) as varchar(5))
FROM c, b
WHERE c.store = b.store
AND c.ln = b.rn
),
H (tn, text) as
(
SELECT 1, cast('Store#' as varchar (1000)) text from sysibm.sysdummy1
UNION ALL
SELECT tn+1, text || ', ' || csg
FROM g, h
WHERE tn = rn
)
SELECT Text
FROM c
WHERE c.ln = (SELECT count(*) from g) + 1
UNION ALL
SELECT text FROM H
WHERE h.tn = (SELECT count(*) from g) + 1;
Hi James,
This should be the last significant change (unless the needs change or I've misunderstood them). I've changed the sub-query names to have some meaning and added the totals column in the final results.
Here's a rundown of each of the sub-queries:
- csg_list -- generate a list of all the CSG names that will appear in the result.
- Store_Summary -- generate a list of all stores, applicable CSGs, and the number of invoices for the store.
- Results - generates the output string for each store.
- Heading - Generates the heading row.
You can see the results of each of them, and the steps taken to build the result by executing portions of the CTE.
- replace the comma and following text after the csg_list definition with "select * from csg_list" to see all of the csg values.
- replace the comma and following text after the Store_Summary definition with "select * from Store_Summary"
- replace the comma and following text after the Results definition with "Select * from Results"
- replace the comma and following text after the Heading definition with "Select * from Heading"
DB2 is very different from SQL Server in handling recursive SQL and CTE structure.
- Some of the tests above will fail on SQL Server as it rejects a CTE where all sub-queries aren't used, making testing difficult.
- As mentioned earlier, DB2 doesn't automatically extend string definitions, SQL Server does.
- DB2 will continue the recursive process until an answer is found or the stack overflows, SQL Server defaults to a depth of 100.
- The two sub-queries that produce the final result will need to be in the other order on SQL Server.
You probably don't need to know that, but if you have a SQL Server person on staff he'll need to know that.
Good Luck!
Kent
This should be the last significant change (unless the needs change or I've misunderstood them). I've changed the sub-query names to have some meaning and added the totals column in the final results.
Here's a rundown of each of the sub-queries:
- csg_list -- generate a list of all the CSG names that will appear in the result.
- Store_Summary -- generate a list of all stores, applicable CSGs, and the number of invoices for the store.
- Results - generates the output string for each store.
- Heading - Generates the heading row.
You can see the results of each of them, and the steps taken to build the result by executing portions of the CTE.
- replace the comma and following text after the csg_list definition with "select * from csg_list" to see all of the csg values.
- replace the comma and following text after the Store_Summary definition with "select * from Store_Summary"
- replace the comma and following text after the Results definition with "Select * from Results"
- replace the comma and following text after the Heading definition with "Select * from Heading"
DB2 is very different from SQL Server in handling recursive SQL and CTE structure.
- Some of the tests above will fail on SQL Server as it rejects a CTE where all sub-queries aren't used, making testing difficult.
- As mentioned earlier, DB2 doesn't automatically extend string definitions, SQL Server does.
- DB2 will continue the recursive process until an answer is found or the stack overflows, SQL Server defaults to a depth of 100.
- The two sub-queries that produce the final result will need to be in the other order on SQL Server.
You probably don't need to know that, but if you have a SQL Server person on staff he'll need to know that.
WITH csg_list (rn, csg) as
(
SELECT row_number () over (order by csg) rn, csg
FROM (SELECT distinct csg FROM a) t3
),
Store_Summary (rn, store, csg, count) as
(
SELECT row_number () over (partition by t0.store order by t0.store, t1.csg) RN, t0.store, t1.csg, t2.count
FROM (select distinct store from a) t0 cross join csg_list t1
LEFT JOIN (SELECT store, csg, count(ordernumber) count FROM a group by store, csg) t2
ON t0.store = t2.store
AND t1.csg = t2.csg
),
Results (ln, store, total, text) as
(
SELECT 1 ln, Store_Summary.store, 0 Total, cast (Store_Summary.store as varchar (400)) FROM Store_Summary WHERE rn = 1
UNION ALL
SELECT ln+1, c.store, Total+coalesce(count,0), text || ', ' || cast (coalesce (count,0) as varchar(5))
FROM Results c, Store_Summary b
WHERE c.store = b.store
AND c.ln = b.rn
),
Heading (tn, text) as
(
SELECT 1, cast('Store#' as varchar (400)) text from sysibm.sysdummy1
UNION ALL
SELECT tn+1, text || ', ' || csg
FROM csg_list, Heading
WHERE tn = rn
)
SELECT Text || ', ' || cast (total as varchar(10))
FROM Results c
WHERE c.ln = (SELECT count(*) from csg_list) + 1
UNION ALL
SELECT text || ', Total'
FROM Heading
WHERE Heading.tn = (SELECT count(*) from csg_list) + 1;
Good Luck!
Kent
ASKER
Kent,
When I run this query, only the header line (I think) is returned (see attachment). What could I be doing wrong?
Thanks everyone for your help.
James
2017-03-23_7-44-18.png
When I run this query, only the header line (I think) is returned (see attachment). What could I be doing wrong?
Thanks everyone for your help.
James
2017-03-23_7-44-18.png
It looks like a data issue. Note that the second item in the header is blank, suggesting one or more store has a blank or NULL for a csg value (I suspect NULL). Let's check it with the first sub-query in the CTE.
WITH csg_list (rn, csg) as
(
SELECT row_number () over (order by csg) rn, csg
FROM (SELECT distinct csg FROM a) t3
)
SELECT * FROM csg_list;
If you see a blank or NULL value, modify the csg_list sub-query to be:
WITH csg_list (rn, csg) as
(
SELECT row_number () over (order by csg) rn, csg
FROM (SELECT distinct csg FROM a WHERE csg <> ' ' AND csg is not NULL) t3
),
WITH csg_list (rn, csg) as
(
SELECT row_number () over (order by csg) rn, csg
FROM (SELECT distinct csg FROM a) t3
)
SELECT * FROM csg_list;
If you see a blank or NULL value, modify the csg_list sub-query to be:
WITH csg_list (rn, csg) as
(
SELECT row_number () over (order by csg) rn, csg
FROM (SELECT distinct csg FROM a WHERE csg <> ' ' AND csg is not NULL) t3
),
ASKER
There was a blank and I modified the query as you instructed. I now get 2 lines of data, the header line and data for the first store. I've attached the results.
Thank you,
James
2017-03-23_8-19-19.png
Thank you,
James
2017-03-23_8-19-19.png
ASKER
When I run just the csg_list and Store_summary subqueries together, I get 932 lines of data, but at line 79 the store# becomes 'Ÿ�����'. I've attached the results.
Thanks,
James
2017-03-23_8-34-40.png
2017-03-23_8-35-19.png
2017-03-23_8-35-45.png
Thanks,
James
2017-03-23_8-34-40.png
2017-03-23_8-35-19.png
2017-03-23_8-35-45.png
That's odd. It looks like there may be binary data in one of the store numbers.
Can you select all of your store numbers from the table, sorting by name? If binary data is in one of the names it should sort to the top and/or bottom of the list.
Also, it looks like we'll need to swap the two items in the final query. DB2 for that level on the AS400 seems to unwind the stack differently than does DB2 on my system. But that's a minor cleanup for the end.
Can you select all of your store numbers from the table, sorting by name? If binary data is in one of the names it should sort to the top and/or bottom of the list.
Also, it looks like we'll need to swap the two items in the final query. DB2 for that level on the AS400 seems to unwind the stack differently than does DB2 on my system. But that's a minor cleanup for the end.
ASKER
Ok, I figured out my mistake and the 'Ÿ�����' is gone. Now I am getting the 2 lines again when running the query. See the attached results.
Thanks,
James
2017-03-23_9-21-47.png
Thanks,
James
2017-03-23_9-21-47.png
ASKER
There are only 4 stores currently in my table: SZ3279, SZD429, UYP003, & UYT001.
ASKER
There are only 4 stores currently in my table: SZ3279, SZD429, UYP003, & UYT001.
There are currently 27 CSGs in my table (BK1 through ZZZ).
There are a total of 15,121 orders in my table.
Thanks,
James
There are currently 27 CSGs in my table (BK1 through ZZZ).
There are a total of 15,121 orders in my table.
Thanks,
James
I'm not sure why we're not seeing all 4 stores. What do you get when you run just the first two sub-queries?
WITH csg_list (rn, csg) as
(
SELECT row_number () over (order by csg) rn, csg
FROM (SELECT distinct csg FROM a) t3
),
Store_Summary (rn, store, csg, count) as
(
SELECT row_number () over (partition by t0.store order by t0.store, t1.csg) RN, t0.store, t1.csg, t2.count
FROM (select distinct store from a) t0 cross join csg_list t1
LEFT JOIN (SELECT store, csg, count(ordernumber) count FROM a group by store, csg) t2
ON t0.store = t2.store
AND t1.csg = t2.csg
)
SELECT * FROM Store_Summary;
and
WITH csg_list (rn, csg) as
(
SELECT row_number () over (order by csg) rn, csg
FROM (SELECT distinct csg FROM a) t3
),
Store_Summary (rn, store, csg, count) as
(
SELECT row_number () over (partition by t0.store order by t0.store, t1.csg) RN, t0.store, t1.csg, t2.count
FROM (select distinct store from a) t0 cross join csg_list t1
LEFT JOIN (SELECT store, csg, count(ordernumber) count FROM a group by store, csg) t2
ON t0.store = t2.store
AND t1.csg = t2.csg
)
SELECT distinct store FROM Store_Summary;
WITH csg_list (rn, csg) as
(
SELECT row_number () over (order by csg) rn, csg
FROM (SELECT distinct csg FROM a) t3
),
Store_Summary (rn, store, csg, count) as
(
SELECT row_number () over (partition by t0.store order by t0.store, t1.csg) RN, t0.store, t1.csg, t2.count
FROM (select distinct store from a) t0 cross join csg_list t1
LEFT JOIN (SELECT store, csg, count(ordernumber) count FROM a group by store, csg) t2
ON t0.store = t2.store
AND t1.csg = t2.csg
)
SELECT * FROM Store_Summary;
and
WITH csg_list (rn, csg) as
(
SELECT row_number () over (order by csg) rn, csg
FROM (SELECT distinct csg FROM a) t3
),
Store_Summary (rn, store, csg, count) as
(
SELECT row_number () over (partition by t0.store order by t0.store, t1.csg) RN, t0.store, t1.csg, t2.count
FROM (select distinct store from a) t0 cross join csg_list t1
LEFT JOIN (SELECT store, csg, count(ordernumber) count FROM a group by store, csg) t2
ON t0.store = t2.store
AND t1.csg = t2.csg
)
SELECT distinct store FROM Store_Summary;
ASKER
When running the first query, I get 78 lines.
When running the second query, I get 4 lines (the 4 store#s).
2017-03-23_10-14-38.png
2017-03-23_10-15-39.png
2017-03-23_10-16-35.png
2017-03-23_10-11-53.png
When running the second query, I get 4 lines (the 4 store#s).
2017-03-23_10-14-38.png
2017-03-23_10-15-39.png
2017-03-23_10-16-35.png
2017-03-23_10-11-53.png
Ok. Something in building the row data isn't behaving as expected. (Probably another data issue.)
What do we get from this:
What do we get from this:
WITH csg_list (rn, csg) as
(
SELECT row_number () over (order by csg) rn, csg
FROM (SELECT distinct csg FROM a) t3
),
Store_Summary (rn, store, csg, count) as
(
SELECT row_number () over (partition by t0.store order by t0.store, t1.csg) RN, t0.store, t1.csg, t2.count
FROM (select distinct store from a) t0 cross join csg_list t1
LEFT JOIN (SELECT store, csg, count(ordernumber) count FROM a group by store, csg) t2
ON t0.store = t2.store
AND t1.csg = t2.csg
),
Results (ln, store, total, text) as
(
SELECT 1 ln, Store_Summary.store, 0 Total, cast (Store_Summary.store as varchar (400)) FROM Store_Summary WHERE rn = 1
UNION ALL
SELECT ln+1, c.store, Total+coalesce(count,0), text || ', ' || cast (coalesce (count,0) as varchar(5))
FROM Results c, Store_Summary b
WHERE c.store = b.store
AND c.ln = b.rn
)
SELECT * FROM Results;
ASKER
When I put 'Select * from Heading' after the first part of the Heading query I get the attached results.
...
Heading (tn, text) as
(
SELECT 1, cast('Store#' as varchar (400)) text from sysibm.sysdummy1
UNION ALL
SELECT tn+1, text || ', ' || csg
FROM csg_list, Heading
WHERE tn = rn
)
Select * from Heading
2017-03-23_10-31-00.png
...
Heading (tn, text) as
(
SELECT 1, cast('Store#' as varchar (400)) text from sysibm.sysdummy1
UNION ALL
SELECT tn+1, text || ', ' || csg
FROM csg_list, Heading
WHERE tn = rn
)
Select * from Heading
2017-03-23_10-31-00.png
That looks normal. The recursive query builds up the row by appending the text to the previously created row. That's why the final query selects the row with the highest ID value, to get only the row with all of the data.
The missing data probably comes from something in the Results sub-query though.
The missing data probably comes from something in the Results sub-query though.
It looks like I didn't carry forward a change to the csg_list sub-query. Let's go back to a baseline.
WITH csg_list (rn, csg) as
(
SELECT row_number () over (order by csg) rn, csg
FROM (SELECT distinct csg FROM a WHERE csg <> ' ' AND csg is not NULL) t3
),
Store_Summary (rn, store, csg, count) as
(
SELECT row_number () over (partition by t0.store order by t0.store, t1.csg) RN, t0.store, t1.csg, t2.count
FROM (select distinct store from a) t0 cross join csg_list t1
LEFT JOIN (SELECT store, csg, count(ordernumber) count FROM a group by store, csg) t2
ON t0.store = t2.store
AND t1.csg = t2.csg
),
Results (ln, store, total, text) as
(
SELECT 1 ln, Store_Summary.store, 0 Total, cast (Store_Summary.store as varchar (400)) FROM Store_Summary WHERE rn = 1
UNION ALL
SELECT ln+1, c.store, Total+coalesce(count,0), text || ', ' || cast (coalesce (count,0) as varchar(5))
FROM Results c, Store_Summary b
WHERE c.store = b.store
AND c.ln = b.rn
),
Heading (tn, text) as
(
SELECT 1, cast('Store#' as varchar (400)) text from sysibm.sysdummy1
UNION ALL
SELECT tn+1, text || ', ' || csg
FROM csg_list, Heading
WHERE tn = rn
)
SELECT text || ', Total'
FROM Heading
WHERE Heading.tn = (SELECT count(*) from csg_list) + 1
UNION ALL
SELECT Text || ', ' || cast (total as varchar(10))
FROM Results c
WHERE c.ln = (SELECT count(*) from csg_list) + 1;
ASKER
From your above question, I get the attached results.
2017-03-23_10-41-26.png
2017-03-23_10-41-53.png
2017-03-23_10-42-16.png
2017-03-23_10-41-26.png
2017-03-23_10-41-53.png
2017-03-23_10-42-16.png
ASKER
From the query in your response 'It looks like I didn't carry forward a change to the csg_list sub-query. Let's go back to a baseline.', I get the attached results.
2017-03-23_10-51-27.png
2017-03-23_10-51-27.png
Man, but I need to apologize. I copied the wrong thing. :( One more time...
WITH csg_list (rn, csg) as
(
SELECT row_number () over (order by csg) rn, csg
FROM (SELECT distinct csg FROM a WHERE csg <> ' ' AND csg is not NULL) t3
),
Store_Summary (rn, store, csg, count) as
(
SELECT row_number () over (partition by t0.store order by t0.store, t1.csg) RN, t0.store, t1.csg, t2.count
FROM (select distinct store from a) t0 cross join csg_list t1
LEFT JOIN (SELECT store, csg, count(ordernumber) count FROM a group by store, csg) t2
ON t0.store = t2.store
AND t1.csg = t2.csg
),
Results (ln, store, total, text) as
(
SELECT 1 ln, Store_Summary.store, 0 Total, cast (Store_Summary.store as varchar (400)) FROM Store_Summary WHERE rn = 1
UNION ALL
SELECT ln+1, c.store, Total+coalesce(count,0), text || ', ' || cast (coalesce (count,0) as varchar(5))
FROM Results c, Store_Summary b
WHERE c.store = b.store
AND c.ln = b.rn
),
Heading (tn, text) as
(
SELECT 1, cast('Store#' as varchar (400)) text from sysibm.sysdummy1
UNION ALL
SELECT tn+1, text || ', ' || csg
FROM csg_list, Heading
WHERE tn = rn
)
SELECT text || ', Total'
FROM Heading
WHERE Heading.tn = (SELECT count(*) from csg_list) + 1
UNION ALL
SELECT Text || ', ' || cast (total as varchar(10))
FROM Results r
WHERE r.ln = (SELECT max(ln) from Result r2 WHERE r.store = r2.store);
ASKER
Looking better. Attached are the results. Can I get it in columns and rows like the 2nd attachment so that the order counts will be below the correct CSG's?
Thank you,
James
2017-03-23_11-30-56.png
2017-03-23_11-34-30.png
Thank you,
James
2017-03-23_11-30-56.png
2017-03-23_11-34-30.png
Ok. This looks pretty good to me. :)
WITH csg_list (rn, csg) as
(
SELECT row_number () over (order by csg) rn, csg
FROM (SELECT distinct csg FROM a WHERE csg <> ' ' AND csg is not NULL) t3
),
Store_Summary (rn, store, csg, count) as
(
SELECT row_number () over (partition by t0.store order by t0.store, t1.csg) RN, t0.store, t1.csg, t2.count
FROM (select distinct store from a) t0 cross join csg_list t1
LEFT JOIN (SELECT store, csg, count(ordernumber) count FROM a group by store, csg) t2
ON t0.store = t2.store
AND t1.csg = t2.csg
),
Results (ln, store, total, text) as
(
SELECT 1 ln, Store_Summary.store, 0 Total, cast (Store_Summary.store as varchar (400)) FROM Store_Summary WHERE rn = 1
UNION ALL
SELECT ln+1, c.store, Total+coalesce(count,0), text || ', ' || cast (coalesce (count,0) as varchar(5))
FROM Results c, Store_Summary b
WHERE c.store = b.store
AND c.ln = b.rn
),
Footing (ln, text) as
(
SELECT 0 ln, cast ('Totals' as varchar (400)) FROM sysibm.sysdummy1
UNION ALL
SELECT ln+1, Text || ', ' || cast ((SELECT coalesce (SUM (count), 0) FROM Store_Summary SS WHERE SS.csg = csg_list.csg) as varchar (10))
FROM csg_list, Footing
WHERE rn = ln+1
),
Heading (tn, text) as
(
SELECT 1, cast('Store#' as varchar (400)) text from sysibm.sysdummy1
UNION ALL
SELECT tn+1, text || ', ' || csg
FROM csg_list, Heading
WHERE tn = rn
)
SELECT text || ', Total'
FROM Heading
WHERE Heading.tn = (SELECT count(*) from csg_list) + 1
UNION ALL
SELECT Text || ', ' || cast (total as varchar(10))
FROM Results r
WHERE r.ln = (SELECT max(ln) from Results r2 WHERE r.store = r2.store)
UNION ALL
SELECT Text || ', ' || cast ((SELECT sum(count) FROM Store_Summary) as varchar(10))
FROM Footing F
WHERE f.ln = (SELECT max(ln) FROM footing);
ASKER
Attached are the results. Can I get it in columns and rows like the 2nd attachment so that the order counts will be aligned below the corresponding CSG's?
Thanks,
James
2017-03-23_13-32-31.png
2017-03-23_11-34-30.png
Thanks,
James
2017-03-23_13-32-31.png
2017-03-23_11-34-30.png
That's a bit trickier, but doable. :) I'll get it done.
The comma separated results are intended for opening/importing into Excel.
Kent
The comma separated results are intended for opening/importing into Excel.
Kent
ASKER
Kent,
How could I import the results into Excel and have the results aligned under the corresponding column headers (CSG's)?
How could I import the results into Excel and have the results aligned under the corresponding column headers (CSG's)?
Save the results in a file (on a PC, not the AS400) with a file extension of .csv. Then open the file with excel.
You can also copy and paste the contents into Excel, but they'll probably all wind up in column A. If so, click on the Data tab and then click on the "Text To Columns" icon.
Either should work just fine.
Kent
You can also copy and paste the contents into Excel, but they'll probably all wind up in column A. If so, click on the Data tab and then click on the "Text To Columns" icon.
Either should work just fine.
Kent
Excel also has a Pivot function. A much simpler query with the CSG headings down the left side and the store numbers across the top is available to us. Once imported into Excel, Click on the Insert tab (icon), then on Pivot Table. Excel should walk you through pivoting the data so that the store numbers are on the left and the CSG values along the top.
ASKER
The problem I'm having with it is that the data is not aligned under the corresponding column header (CSG). When I put it into Excel, it all goes into column A as you said. When I select column A and then click on the Data tab and then click on the "Text To Columns" icon, and I convert the text to columns using commas as the delimiter, the data is separated in columns from left to right but not aligned in the correct cells under the corresponding column headers (CSG's). The data is just filled into the cells from left to right. Hope that makes sense.
Thanks,
James
Thanks,
James
It does make sense.
Let me check the results and make sure that they line up in the text we're producing.
Let me check the results and make sure that they line up in the text we're producing.
ASKER
Not every store has an order for each CSG. Maybe we could put zeros in where there's no orders?
Using COALESCE() to put zeros into the sums in place of NUL values is a good idea when possible.
Hi James,
Using my (admittedly limited) sample data everything lines up fine. And I see zeroes (not NULL or blank).
When you select "Text to Columns", make sure you also select "delimited" and that the comma is the delimiter.
Also, when I save the output to a file as "sample.csv" and open it with Excel it also formats correctly.
Kent
Using my (admittedly limited) sample data everything lines up fine. And I see zeroes (not NULL or blank).
When you select "Text to Columns", make sure you also select "delimited" and that the comma is the delimiter.
Also, when I save the output to a file as "sample.csv" and open it with Excel it also formats correctly.
Kent
ASKER
Kent,
Thank you. I did get it to format correctly in Excel. I am working with QMF and trying to get the results to export into Excel with a procedure like many of my other queries. Is there a way to have the data display in QMF like one of the examples in the attached file so that it doesn't need manual formatting in Excel?
Thanks again,
James
Thank you. I did get it to format correctly in Excel. I am working with QMF and trying to get the results to export into Excel with a procedure like many of my other queries. Is there a way to have the data display in QMF like one of the examples in the attached file so that it doesn't need manual formatting in Excel?
Thanks again,
James
ASKER
Attached are how the results currently look in QMF
2017-03-24_7-48-53.png
2017-03-24_7-48-53.png
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Attached are the results from your above query.
2017-03-24_11-27-43.png
2017-03-24_11-27-43.png
Let's see if Tom (tliotta) is still lurking here.
When I run that on my version of DB2, I get the expected results (below). Of course, I don't have QMF available to me. I suspect that QMF is compacting consecutive spaces, but I can't test or prove that.
Tom. Do you have an idea of what's happening or what our next step should be?
Thanks,
Kent
When I run that on my version of DB2, I get the expected results (below). Of course, I don't have QMF available to me. I suspect that QMF is compacting consecutive spaces, but I can't test or prove that.
Tom. Do you have an idea of what's happening or what our next step should be?
Thanks,
Kent
Store# | C01 | C02 | C03 | C04 | Total
AAA000001 | 2 | 0 | 0 | 0 | 2
BBB000001 | 1 | 2 | 0 | 0 | 3
CCC000001 | 2 | 0 | 1 | 1 | 4
DDD000001 | 3 | 1 | 1 | 0 | 5
Totals | 8 | 3 | 2 | 1 | 14
ASKER
Ok, thank you very much Kent
Also no "QMF" here, but I'll set up DB2 with test data above (this evening perhaps) and see what happens in a couple different clients. I see at least one change in test data that could help narrow down any issue. I'll post back when I know something.
No good news from me. Exact match with Kent's results. I also made a minor change to Kent's test data to verify an odd possibility, but the result was still perfect.
We need to see the exact query that gets the bad result. Please post it as code (or text) and not as an image because we'll need to edit it.
We need to see the exact query that gets the bad result. Please post it as code (or text) and not as an image because we'll need to edit it.
ASKER
Sorry, I've been on vacation. I will be posting the query soon.
Thanks again
Thanks again
ASKER
Here is my query:
WITH consol_sel_grp_list (rn, csg) as
(
SELECT row_number () over (order by csg) rn,
csg
FROM (SELECT distinct csg FROM ddtcenvr.mrr
WHERE csg <> ' ' AND csg is not NULL
and doc_id = 'A5J') t3
),
Store_Summary (rn, store, csg, count)
as
(
SELECT row_number () over (partition by t0.store
order by t0.store, t1.csg) RN, t0.store,
t1.csg, t2.count
FROM (select distinct store from ddtcenvr.mrr
where doc_id = 'A5J') t0 cross join csg_list t1
LEFT JOIN (SELECT store, csg,
count(ordernumber) count FROM ddtcenvr.mrr where doc_id = 'A5J'
group by store, csg) t2
ON t0.store = t2.store
AND t1.csg = t2.csg
),
Results (ln, store, total, text) as
(
SELECT 1 ln, Store_Summary.store, 0 Total,
cast (cast (Store_Summary.store as char(10))
as varchar (400)) FROM Store_Summary WHERE rn = 1
UNION ALL
SELECT ln+1, c.store, Total+coalesce(count,0), text || ' | ' ||
right (' ' || cast (coalesce (count,0) as varchar(8)), 8)
FROM Results c, Store_Summary b
WHERE c.store = b.store
AND c.ln = b.rn
),
Footing (ln, text) as
(
SELECT 0 ln, cast (cast ('Totals' as char(10)) as varchar (400))
FROM sysibm.sysdummy1
UNION ALL
SELECT ln+1, Text || ' | ' || right (' ' ||
cast ((SELECT coalesce (SUM (count), 0)
FROM Store_Summary SS
WHERE SS.csg = csg_list.csg)
as varchar (8)), 8)
FROM csg_list, Footing
WHERE rn = ln+1
),
Heading (tn, text) as
(
SELECT 1, cast(cast ('Store#' as char(10))
as varchar (400)) text
from sysibm.sysdummy1
UNION ALL
SELECT tn+1, text || ' | ' || cast (csg as char(8))
FROM csg_list, Heading
WHERE tn = rn
)
SELECT text || ' | Total'
FROM Heading
WHERE Heading.tn = (SELECT count(*) from csg_list) + 1
UNION ALL
SELECT Text || ' | ' || right (' ' || cast (total as varchar(8)), 8)
FROM Results r
WHERE r.ln = (SELECT max(ln) from Results r2
WHERE r.store = r2.store)
UNION ALL
SELECT Text || ' | ' || right (' ' || cast ((SELECT sum(count)
FROM store_Summary) as varchar(10)), 8)
FROM Footing F
WHERE f.ln = (SELECT max(ln) FROM footing)
Thanks everyone,
James
WITH consol_sel_grp_list (rn, csg) as
(
SELECT row_number () over (order by csg) rn,
csg
FROM (SELECT distinct csg FROM ddtcenvr.mrr
WHERE csg <> ' ' AND csg is not NULL
and doc_id = 'A5J') t3
),
Store_Summary (rn, store, csg, count)
as
(
SELECT row_number () over (partition by t0.store
order by t0.store, t1.csg) RN, t0.store,
t1.csg, t2.count
FROM (select distinct store from ddtcenvr.mrr
where doc_id = 'A5J') t0 cross join csg_list t1
LEFT JOIN (SELECT store, csg,
count(ordernumber) count FROM ddtcenvr.mrr where doc_id = 'A5J'
group by store, csg) t2
ON t0.store = t2.store
AND t1.csg = t2.csg
),
Results (ln, store, total, text) as
(
SELECT 1 ln, Store_Summary.store, 0 Total,
cast (cast (Store_Summary.store as char(10))
as varchar (400)) FROM Store_Summary WHERE rn = 1
UNION ALL
SELECT ln+1, c.store, Total+coalesce(count,0), text || ' | ' ||
right (' ' || cast (coalesce (count,0) as varchar(8)), 8)
FROM Results c, Store_Summary b
WHERE c.store = b.store
AND c.ln = b.rn
),
Footing (ln, text) as
(
SELECT 0 ln, cast (cast ('Totals' as char(10)) as varchar (400))
FROM sysibm.sysdummy1
UNION ALL
SELECT ln+1, Text || ' | ' || right (' ' ||
cast ((SELECT coalesce (SUM (count), 0)
FROM Store_Summary SS
WHERE SS.csg = csg_list.csg)
as varchar (8)), 8)
FROM csg_list, Footing
WHERE rn = ln+1
),
Heading (tn, text) as
(
SELECT 1, cast(cast ('Store#' as char(10))
as varchar (400)) text
from sysibm.sysdummy1
UNION ALL
SELECT tn+1, text || ' | ' || cast (csg as char(8))
FROM csg_list, Heading
WHERE tn = rn
)
SELECT text || ' | Total'
FROM Heading
WHERE Heading.tn = (SELECT count(*) from csg_list) + 1
UNION ALL
SELECT Text || ' | ' || right (' ' || cast (total as varchar(8)), 8)
FROM Results r
WHERE r.ln = (SELECT max(ln) from Results r2
WHERE r.store = r2.store)
UNION ALL
SELECT Text || ' | ' || right (' ' || cast ((SELECT sum(count)
FROM store_Summary) as varchar(10)), 8)
FROM Footing F
WHERE f.ln = (SELECT max(ln) FROM footing)
Thanks everyone,
James
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
I will see if I can try it out on another client.
Thanks everyone
Thanks everyone
Can you provide a brief description of what the table(s) look like?
Thanks...