Attributes · Gotchas & Edge Cases · Practice Question
Why xasc on Two Columns Only Marks the First Column as Sorted
Question
After sorting an in-memory table by two columns:
t:([] sym:5?`A`B`C; time:5?23:59:59.999)
`sym`time xasc `tWhy does the s# (sorted) attribute appear on sym but not on time?
Solution
t:([] sym:5?`A`B`C; time:5?23:59:59.999)
`sym`time xasc `t
attr t`sym / `s — sorted attribute applied
attr t`time / ` — no attributetime is sorted only within each sym group, not globally across the whole column. The s# attribute requires global sortedness, which time does not have after a multi-column sort.
Explanation
s# is a claim that every element of the vector is less than or equal to the next:
t[`time] 0 <= t[`time] 1 <= t[`time] 2 <= ...After `sym`time xasc `t, time is ordered only relative to sym. Crossing a sym boundary can reset the time order entirely:
sym time
---------
A 02:14:00 / time resets when sym changes
A 17:45:00
B 00:03:00 / <-- less than previous row's time
B 09:22:00
C 11:58:00Row 3's time (00:03:00) is less than row 2's time (17:45:00), so the column is not globally sorted. Applying s# would be incorrect — kdb+ would use binary search on the time column and return wrong results because it would assume no smaller value exists beyond a given point.
kdb+ only applies s# to the first sort key (sym here) because that column is genuinely sorted from top to bottom after xasc. Subsequent sort keys are only locally sorted within each group of equal first-key values, which does not satisfy the global invariant that s# requires.
Practical consequence: queries that filter on time alone after a sym+time sort still scan the whole time column. If fast time-range lookups across all symbols matter more than grouping by symbol, sort by time alone instead (`time xasc `t gives time the sorted attribute). Joins such as aj are designed for the sym+time layout: they find each symbol's rows first and then search by time within them.