# Perspective Report Crosstab Column Sorting

**URL:** <https://forum.inductiveautomation.com/t/perspective-report-crosstab-column-sorting/117663>\
**Category:** Ignition\
**Tags:** perspective, reporting\
**Created:** [October 29, 2026, 2:32pm UTC](https://forum.inductiveautomation.com/t/perspective-report-crosstab-column-sorting/117663 "2026-10-29T14:32:24Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![Daniel\_Calderon](https://avatars.discourse-cdn.com/v4/letter/d/51bf81/32.png) [@Daniel\_Calderon](https://forum.inductiveautomation.com/u/Daniel_Calderon)\
**Post date:** [October 29, 2026, 2:32pm UTC](https://forum.inductiveautomation.com/t/perspective-report-crosstab-column-sorting/117663/1 "2026-10-29T14:32:24Z")

</div>

Does anyone know how the crosstab table component decides how it sorts rows and columns? and if it's even possible to control it? Thanks.

Editted a typo

---

<div class="post-metadata">

**Author:** ![gunnar.newell](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/gunnar.newell/32/87635_2.png) [@gunnar.newell](https://forum.inductiveautomation.com/u/gunnar.newell)\
**Post date:** [October 29, 2026, 3:27pm UTC](https://forum.inductiveautomation.com/t/perspective-report-crosstab-column-sorting/117663/2 "2026-10-29T15:27:21Z")

</div>

I don't use the CrossTab componenet much, but I would try presorting the data before you populate it into the crosstab.

---

<div class="post-metadata">

**Author:** ![Daniel\_Calderon](https://avatars.discourse-cdn.com/v4/letter/d/51bf81/32.png) [@Daniel\_Calderon](https://forum.inductiveautomation.com/u/Daniel_Calderon)\
**Post date:** [October 29, 2026, 7:07pm UTC](https://forum.inductiveautomation.com/t/perspective-report-crosstab-column-sorting/117663/3 "2026-10-29T19:07:08Z")

</div>

It ignores whatever order you pass and sorts its own way. Which I am trying to figure our and control.

---

<div class="post-metadata">

**Author:** ![gunnar.newell](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/gunnar.newell/32/87635_2.png) [@gunnar.newell](https://forum.inductiveautomation.com/u/gunnar.newell)\
**Post date:** [October 29, 2026, 10:20pm UTC](https://forum.inductiveautomation.com/t/perspective-report-crosstab-column-sorting/117663/4 "2026-10-29T22:20:54Z")

</div>

I played around with it a bit.

It looks like it does first appearance sorting. The [Documentation on CrossTabs](https://www.docs.inductiveautomation.com/docs/8.1/appendix/components/report-design-components/report-design-table-palette/report-component-crosstab-table#table-properties) doesn't look like it gives us any detail on sorting order.

I used the example data in the documentation, and I could only ever get it to sort in order of appearance.

So this data

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/e/c/ecbf730d45de4ba7a2459626909126f840522f82.png)

has this cross tab

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/7/f/7fadf6fff960e5be3430ae9f94db0e039d850691.png)

and this data

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/d/5/d5add4000ee1148d63263385bd21dd1b3ba1c81e.png)

has this cross tab

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/d/5/d5d5ffdc4004d6094838e32864028983cf73a75f.png)

note I'm not doing any summing for the values they are also by first occurance.

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [October 29, 2026, 10:26pm UTC](https://forum.inductiveautomation.com/t/perspective-report-crosstab-column-sorting/117663/5 "2026-10-29T22:26:07Z")

</div>

It's not possible\[1\] to set explicit sorting for cross tabs. I'm really surprised no one has noticed this before.

That said, along the lines of [previous nerd sniping](https://forum.inductiveautomation.com/t/pdf-report-linking/18860/2), it is at least _technically_ possible. I would not advise anyone actually do this.

> ****
>
> Given crosstab data like this:
> 
> ```python
> # Faux row-level data for a Reporting Cross Tab.
> headers = ['Region', 'Product', 'Quarter', 'Sales', 'Units']
> rows = [
> ['North', 'Widgets', 'Q1', 12500.00, 118],
> ['North', 'Widgets', 'Q2', 14750.00, 136],
> ['North', 'Widgets', 'Q3', 13925.00, 129],
> ['North', 'Widgets', 'Q4', 16200.00, 151],
> ['North', 'Gadgets', 'Q1', 9800.00, 92],
> ['North', 'Gadgets', 'Q2', 11125.00, 105],
> ['North', 'Gadgets', 'Q3', 10875.00, 101],
> ['North', 'Gadgets', 'Q4', 12400.00, 116],
> ['South', 'Widgets', 'Q1', 10350.00, 99],
> ['South', 'Widgets', 'Q2', 11900.00, 112],
> ['South', 'Widgets', 'Q3', 13250.00, 126],
> ['South', 'Widgets', 'Q4', 14100.00, 134],
> ['South', 'Gadgets', 'Q1', 8700.00, 83],
> ['South', 'Gadgets', 'Q2', 9400.00, 89],
> ['South', 'Gadgets', 'Q3', 10150.00, 96],
> ['South', 'Gadgets', 'Q4', 11325.00, 108],
> ['East', 'Widgets', 'Q1', 15100.00, 143],
> ['East', 'Widgets', 'Q2', 16850.00, 159],
> ['East', 'Widgets', 'Q3', 17400.00, 165],
> ['East', 'Widgets', 'Q4', 18950.00, 180],
> ['East', 'Gadgets', 'Q1', 10725.00, 102],
> ['East', 'Gadgets', 'Q2', 12100.00, 114],
> ['East', 'Gadgets', 'Q3', 11650.00, 110],
> ['East', 'Gadgets', 'Q4', 13575.00, 128],
> ['West', 'Widgets', 'Q1', 9300.00, 88],
> ['West', 'Widgets', 'Q2', 10450.00, 99],
> ['West', 'Widgets', 'Q3', 11225.00, 106],
> ['West', 'Widgets', 'Q4', 12750.00, 121],
> ['West', 'Gadgets', 'Q1', 7600.00, 73],
> ['West', 'Gadgets', 'Q2', 8350.00, 80],
> ['West', 'Gadgets', 'Q3', 9100.00, 87],
> ['West', 'Gadgets', 'Q4', 9950.00, 95]
> ]
> 
> data['crossTabData'] = system.dataset.toDataset(headers, rows)
> 
> ```
> 
> And a script like this in the script console:
> 
> ```python
> # grouping key -> list of (sort key, "ascend" | "descend")
> SORTS = {
> "Quarter": [("total.Sales", "descend")],
> "Product": [("Product", "ascend")],
> }
> 
> from java.awt import Toolkit
> from java.awt.datatransfer import DataFlavor, Transferable, UnsupportedFlavorException
> from java.io import ByteArrayInputStream
> from java.lang import String as JString
> import xml.etree.ElementTree as ET
> 
> RM_FLAVOR = DataFlavor("application/reportmill", "ReportMill Shape Data")
> 
> class ShapeTransferable(Transferable):
> def __init__ (self, xmlString):
> self.bytes = JString(xmlString).getBytes("UTF-8")
> 
> def getTransferDataFlavors(self):
> return [RM_FLAVOR, DataFlavor.stringFlavor]
> 
> def isDataFlavorSupported(self, flavor):
> return flavor in self.getTransferDataFlavors()
> 
> def getTransferData(self, flavor):
> if flavor.equals(RM_FLAVOR):
> return ByteArrayInputStream(self.bytes)
> if flavor.equals(DataFlavor.stringFlavor):
> return JString(self.bytes, "UTF-8")
> raise UnsupportedFlavorException(flavor)
> 
> clipboard = Toolkit.getDefaultToolkit().getSystemClipboard()
> root = ET.fromstring(clipboard.getData(DataFlavor.stringFlavor).encode("utf-8"))
> 
> for grouping in root.iter("grouping"):
> sorts = SORTS.get(grouping.get("key"))
> if sorts is None:
> continue
> for existing in grouping.findall("sort"):
> grouping.remove(existing)
> for key, order in sorts:
> attrs = {"key": key}
> if order == "descend":
> attrs["order"] = "descend"
> ET.SubElement(grouping, "sort", attrs)
> print "Set sorts on grouping %r: %s" % (grouping.get("key"), sorts)
> 
> clipboard.setContents(ShapeTransferable(ET.tostring(root)), None)
> 
> ```
> 
> If you:
> 
> 1. Create your own crosstab
> 2. Update the script in the console with appropriate keys and sorting decisions
> 3. Copy the cross tab element directly in the report designer
> 4. Execute the code in the script console
> 5. Paste the updated crosstab back in to the report
> 
> You will, in fact, get a "sorted" crosstab:
> 
> ![Screenshot 2026-10-29 at 3.25.30 PM](https://us1.discourse-cdn.com/inductiveautomation/original/3X/8/7/8783924918bba66e5a5ad809e7c282b1a26867ec.png)

* * *

1. or at least not practical
