How to Query Infor LN by Company When the Company Number Is in the Table Name
One Infor LN table lands in your warehouse as many tables, one per company, and not one of them has a company column. The semantic model is where the company becomes a column you can group by.
Query your Infor LN data with AI against a replicated database, ask what each company sold last quarter, and the question turns out not to be expressible. Infor puts the company number in the physical table name. One logical sales-order-lines table lands in your warehouse as several tables that differ only by a numeric suffix, and not one of them carries a column saying which company it is. So an agent reasoning over columns has nothing to group by, and an agent that picks a single table returns a confident number for one company out of several.
Before you start
- An LN database already replicated into a warehouse. No managed application connector exists for Infor.
fivetran.com/docs/connectors/applications/inforanddocs.airbyte.com/integrations/sources/infor-lnboth return 404, re-checked this morning; they're cited rather than linked, because the 404 is the finding. LN runs on SQL Server or Oracle, so the route is a database connector such as Fivetran's SQL Server connector. - Or Infor's own route. LN publishes to Data Lake over ION messaging, extracted through Data Fabric.
- No free instance exists. Infor ships no free or developer-tier LN, so every query here runs against your own estate.
- Read access to
information_schemain your destination. That's where the one thing you can't assume gets settled. - Which company numbers are production. That's institutional knowledge, and it's the one input no query supplies.
The question
"What did each of our companies sell last quarter?"
In a multi-company LN estate that's the ordinary monthly question, and a finance team expects it to be a group-by. The data is all in the warehouse. Every row of it landed.
What breaks
Point an agent at the replicated schema and ask. It finds a sales-order-lines table, reads the columns, filters on the invoice date, and writes what a competent analyst would write:
-- WRONG: one company's order lines, reported as the company's order lines.
SELECT COUNT(*) AS invoiced_lines,
COUNT(DISTINCT t_orno) AS orders
FROM tdsls401300
WHERE t_invd <> 0
AND t_invd >= '2026-07-01'
AND t_invd < '2026-10-01';That query runs. It's fast, the column names are right, and the result is a plausible number. It's also one company's activity presented as the business's activity, and nothing in the output says so.
Ask the agent to group by company instead and it can't. No column in tdsls401300 records a company. The grouping key the question asks for isn't in the data; it's in the table's name.
Why it breaks
The company number is part of the table name
Infor specifies LN's physical table names, and the specification is explicit. From the Technical Reference Guide for the SQL Server Database Driver, release 10.7, page 16:
The name of an LN table stored in SQL Server has the following format.t<Package><Module><Table number><Company number>
The same guide defines the last component:
Company Number Within LN, three digit company numbers are used to isolate datasets used for different purposes (for example; demo data, training data, production data). There must be a company with the number 000. Company 000 contains master data common to all companies.
and gives its own worked example:
For example, the table 999 in module adv with company number 000 is created in SQL Server as tttadv999000.
This isn't a quirk of one database engine. Infor states the same rule in its Oracle, DB2, and EnterpriseDB driver guides, each in its own wording and each with a Company Number component. One wording difference matters and is worth recording: only the SQL Server guide describes company numbers as isolating demo, training, and production data. The other three say the company number differentiates areas of functionality, which is a weaker claim.
Decoding tdsls401 with that rule gives package td for Distribution, module sls for Sales, and table number 401. So tdsls401300 and tdsls401500 are two physical tables with identical structure, identical grain, and different data. A database connector replicates tables as the database names them, so all of this arrives in your warehouse exactly as LN wrote it. An application connector would have had the chance to normalise it away. There isn't one.
Infor's own analytics product gives the trap away
The clearest evidence that this is a real gap rather than a naming preference is in Infor's own BI mapping. The LN Analytics mapping guide binds each Birst attribute to its source, and the notation is consistent: a genuine column is written table.column, with a dot.
| Attribute | Mapping |
|---|---|
| Sales Order | tdsls401.orno |
| Invoice Date | tdsls401.invd |
| Order Date | tdsls401.odat |
| Planned Delivery Date | tdsls401.ddta |
| Company | tdsls401_compnr |
Every attribute on that page uses a dot. Company uses an underscore. The Sales Order dimension page does the same thing with tdsls400_compnr beside tdsls400.orno. Infor's own analytics product can't dot into a company column, because the column doesn't exist, so it manufactures the attribute and its notation records the difference.

Every real column in Infor's mapping guide is bound with a dot. The company attribute, on all three pages that publish one, is bound with an underscore.
Three moves, none of them safe
Picking one table omits silently. tdsls401300 is fully populated and plausible. A query against it answers for one company and the result carries no signal about the rest.
Unioning every match overcounts. Sweeping in every tdsls401* table pulls in the demo and training companies that Infor's own guide says are there. The total comes out high, by an amount nobody can estimate without first knowing which suffixes are real.
The suffix doesn't always mean what it looks like. LN has a table-sharing feature, and Infor's page on logical and physical company numbers states it plainly:
Logical tables belong to a logical company but are physically stored under another company.
Example Logical company : 100 Physical company: 500
User 1 works under company 100, which means that he works with the data of company 100. On forms and menus, the user sees: Company 100. Physically, the data of the tables of the Item Based Data module is stored under company 500.
So for a shared module, the number in the table name is the physical company and the logical company appears nowhere in the name. A rule that says "the suffix is the business unit" is correct until somebody enables table sharing, and then it's quietly wrong.
Two more things the schema won't help with
Nothing is ever NULL. The SQL Server guide, page 19: "All columns created by the LN MSQL driver have the NOT NULL constraint. LN does not currently support NULL values in the data." The Oracle, DB2, and EnterpriseDB guides all say the same. Absence is a sentinel instead, which is why Infor's own mapping defines Invoiced as tdsls401.invd <> 0 rather than as a null check. An agent that writes WHERE invoice_date IS NOT NULL gets every row back.
That sentinel is a real date. The same page: "The LN date 0 is mapped to the earliest possible date in the SQL Server (01-Jan-1753)." On Oracle it maps to 4712 B.C. An average-days-to-ship calculation over unshipped orders produces nonsense, and different nonsense on each database.
Infor contradicts itself on the suffix width. The driver guides say "three digit company numbers". Infor's Creating the first company guide says of the same field: "this number must contain four digits." Both are current Infor pages, read this morning. Settling it needs a live instance, so nothing below parses a fixed-width suffix.
That last page carries one more number worth keeping. Of the Create Tables step, Infor notes: "LN creates more than 2000 tables. This process can take some time." That's per company. Your warehouse holds more than two thousand tables for each company, and not one of them has a company column.
A third party reaches the same conclusion independently. NAZDAQ, which sells LN reporting tools, describes the problem in its own words: "tables of the same type residing in different companies are stored as different tables. This means that when you build a report for, say, the purchase orders of company 300, you will use table tdpur041300 (the last 3 characters specify the number of the company in the database.)"
Why nobody inside LN ever noticed
Inside the application, none of this is visible. A user works under a company and LN resolves every table name for them, including the shared-module redirection above: "On forms and menus, the user sees: Company 100."
So the person who asks what each company sold has spent years in a product where the company was a mode rather than a column, and was never once asked to think about it. The schema fact appears only when the data is replicated, and it appears to a different person.
The fix
The semantic model's job here is to state what the schema can't: which physical tables are one logical entity, which company each one holds, and which of them are real.
# Infor LN sales order lines, declared across companies.
# Company numbers are per-deployment. Discover them with the first query in
# "Reproduce it yourself" and have a person confirm which are production.
tables:
- name: sales_order_lines
description: >
Sales order lines from Infor LN (data-dictionary table tdsls401),
unioned across the production companies only. The company number is
part of the physical table name and is projected here as a column.
# Source for the naming rule: Infor Enterprise Server Technical Reference
# Guide for SQL Server Database Driver 10.7, p16:
# t<Package><Module><Table number><Company number>
union_of:
- physical_table: tdsls401300
constants: { company_number: "300" }
- physical_table: tdsls401500
constants: { company_number: "500" }
excluded_physical_tables:
# Demo and training companies. SQL Server driver guide p16: company
# numbers isolate "demo data, training data, production data".
- tdsls401000 # company 000 holds master data common to all companies
- tdsls401100 # confirmed a demo estate by a person, not by a query
columns:
- name: company_number
type: string
description: >
The PHYSICAL company this row was stored under. Not necessarily the
logical company the business uses: under table sharing, a module's
tables live under another company number.
# Source: "To define logical and physical company numbers", 10.7
- name: t_orno
maps_to: order_number
description: Sales order number. On Oracle this column is t$orno.
# Source: LN Analytics mapping guide, Sales Order Line dimension,
# "Sales Order | tdsls401.orno"
- name: t_invd
maps_to: invoice_date
description: >
Invoice date. NOT NULL, like every LN column, and 0 means never
invoiced rather than unknown.
# Sources: SQL Server driver guide p19; LN Analytics mapping guide,
# which defines Invoiced as "tdsls401.invd <> 0"
default_filter_candidate: "t_invd <> 0"
metrics:
- name: invoiced_sales_order_lines
calculation: COUNT(sales_order_lines.*)
required_filters:
- "t_invd <> 0"
grain: one row per sales order line
description: >
Invoiced sales order lines across production companies. Grouping by
company_number is valid because the column is declared here. It does
not exist in any of the underlying tables.Three things that declaration buys, in this post's own terms:
company_numberexists as a column, so "by company" becomes a group-by.- The demo and training companies are excluded by name, once, in a file a person signed, instead of by a filter somebody has to remember.
t_invd <> 0travels with the metric, so the 1753 sentinel can't quietly enter an average.
One thing deliberately missing from that YAML: a revenue metric. Infor publishes column codes for dates, identifiers, and codes, and no amount column for tdsls401 appears anywhere in the mapping guide. Rather than guess a four-letter code, bind yours after the second discovery query below.
Reproduce it yourself
No free LN instance exists, so this runs against your own warehouse. Three steps, and the first two are read-only.
Step one: find out how many companies your warehouse actually holds. Don't assume the suffix width, because Infor's own pages disagree about it. Read it off the result.
-- Every physical sales-order-lines table the replication landed.
SELECT table_schema,
table_name
FROM information_schema.tables
WHERE table_name LIKE 'tdsls401%'
ORDER BY table_name;The row count is the number of companies in scope, and it's a number this post can't supply because it's a property of your estate. Infor's list of defined companies lives in the Companies (ttaad1100m000) session, which is where the names behind those numbers are. Join the suffixes you found against that to see which are production.
Step two: get the column spellings on your release. The column prefix differs by database, so this is a catalogue query rather than a list to trust. The SQL Server guide, page 17: "when an LN column name is created in the SQL Server, it is preceded by the string t_. For example, the LN column with the name cpac is created in the SQL Server with the name t_cpac." On Oracle the prefix is t$, and Oracle then uppercases the name when it stores it in the dictionary.
-- Column names on one landed sales-order-lines table.
SELECT column_name,
data_type
FROM information_schema.columns
WHERE table_name = 'tdsls401300' -- a suffix from step one
ORDER BY ordinal_position;This is also where you find your amount column, which is the one identifier this post won't print.
Step three: run the question both ways and compare. The first query is what an agent writes against the raw schema. The second is what the declaration above makes possible.
-- RIGHT: the declared production companies, with company as a real column.
WITH order_lines AS (
SELECT '300' AS company_number, t_orno AS order_number, t_invd AS invoice_date
FROM tdsls401300
UNION ALL
SELECT '500' AS company_number, t_orno AS order_number, t_invd AS invoice_date
FROM tdsls401500
)
SELECT company_number,
COUNT(*) AS invoiced_lines,
COUNT(DISTINCT order_number) AS orders
FROM order_lines
WHERE invoice_date <> 0
AND invoice_date >= '2026-07-01'
AND invoice_date < '2026-10-01'
GROUP BY company_number
ORDER BY company_number;300 and 500 stand for whatever step one returned and a person confirmed is production. The gap between this result and the single-table query is your own exposure. Swap COUNT(*) for a sum over the amount column you found in step two and the same shape answers the revenue question.
Two things to settle with a person before signing any of this off. Which company numbers are production, which is institutional knowledge rather than anything discoverable. And whether table sharing is enabled, and for which modules, because where it is, the physical suffix isn't the logical company and the mapping has to be declared explicitly.
What this looks like in Agami
Agami is a trust layer between an AI assistant and your warehouse. A company number that lives in a table name is a clean example of what that buys, because it's a case where no amount of prompting helps: the information an agent needs isn't in the data it can see.
- A declared union turns several physical tables into one queryable entity. Naming
tdsls401300andtdsls401500as onesales_order_linesentity, withcompany_numberprojected as a constant per table, is what makes "by company" a group-by. The knowledge lives in one reviewed file instead of in one engineer's memory. - Excluded tables are refused by name rather than by convention. The demo and training suffixes are listed once, as exclusions, and those rules are enforced where the query runs instead of being suggested in a prompt. That's what makes the scope auditable: a question that would have reached
tdsls401100doesn't, and the refusal is recorded. - A required filter rides with the metric, so the sentinel date can't leak in.
t_invd <> 0is part of the metric definition rather than something the next person has to remember about 1753. - Receipts record which declarations an answer used. When somebody questions a company's total next quarter, the trail says which suffixes were in scope and which were excluded. The argument becomes one about the declaration rather than one about the number.
- The declaration is portable across LLMs and databases. It's YAML, so the next assistant or warehouse inherits it. Fixing this inside one BI tool's semantic layer helps that tool alone, which is exactly what Infor's own mapping guide shows happening.
What Agami can't do is tell you which of your company numbers are real. That's a sign-off, and it should be asked for by name rather than inferred. We also have no measurement of how far off a single-table query runs on any LN estate, because we have no LN warehouse behind this post. The queries above are how you get your own number, and the sign-off is the part no query can do.
Frequently asked questions
Why does my Infor LN warehouse have several copies of the same table? Because the company number is part of the physical table name. Infor's Technical Reference Guide for the SQL Server Database Driver specifies the format as t<Package><Module><Table number><Company number>, so one logical table becomes one physical table per company. A database connector replicates them as named, and no application connector exists to normalise it away.
Which Infor LN table suffix is my production company? No query can tell you, and that's the honest answer. The suffixes are discoverable by selecting table names from information_schema.tables, and the names behind the numbers are in the Companies (ttaad1100m000) session. Which ones hold production rather than demo or training data is institutional knowledge, and it's the single sign-off a semantic model needs here.
Is the company number three digits or four? Infor's own pages disagree. The driver guides for all four supported databases say "three digit company numbers", and every worked example is three digits. The 2024.x guide on creating the first company says of the company number "this number must contain four digits". Don't parse a fixed width. Enumerate the table names instead.
Can I just union every table that matches the pattern? No, and that's the trap's second half. The SQL Server driver guide says company numbers isolate "demo data, training data, production data", so a blanket union pulls in estates that were never real. The total comes out high by an amount you can't estimate until you know which suffixes are production.
Why doesn't a date filter behave the way I expect? Because LN has no nulls. All four driver guides state that every column the driver creates carries a NOT NULL constraint, and the LN date 0 maps to 01-Jan-1753 on SQL Server. An unset invoice date is a real date in 1753 rather than a null, which is why Infor's own analytics mapping defines Invoiced as tdsls401.invd <> 0.
References
- Infor Enterprise Server Technical Reference Guide for SQL Server Database Driver 10.7 (PDF). The primary source. Table naming on page 16, column prefix on page 17, and the NOT NULL constraint plus the 1753 date on page 19. Re-read on 2026-10-09.
- Infor Enterprise Server Technical Reference Guide for Oracle Database Driver 10.7 (PDF). The same rule, independently worded, with the
t$column prefix and Oracle's uppercase conversion. - Infor Enterprise Server Technical Reference Guide for DB2 Database Driver 10.7 (PDF) and for EnterpriseDB 10.7 (PDF). The third and fourth statements of the same naming rule, which is what makes it a property of LN rather than of one engine.
- To define logical and physical company numbers. The table-sharing exception, with Infor's own worked example of logical company 100 stored under physical company 500.
- Creating the first company. Source of "LN creates more than 2000 tables", and of the four-digit company number that contradicts the driver guides.
- Infor LN Analytics mapping guide, Sales Order Line. The published column codes, and
tdsls401_compnrwith its underscore. - Infor LN Analytics mapping guide, Sales Order.
tdsls400_compnrbesidetdsls400.orno, so the notation holds on both pages. - Infor LN Analytics mapping guide, Sales Order Line Detail. Where
Invoicedis defined astdsls401.invd <> 0. - Infor Data Fabric, extracting from Data Lake. The second route out, through ION messaging.
- Fivetran SQL Server connector. The route that exists, given there's no application connector.
fivetran.com/docs/connectors/applications/inforanddocs.airbyte.com/integrations/sources/infor-ln. Both return 404, which is the evidence that no managed application connector exists, so they're cited rather than linked. Worth re-checking yourself, because catalogs move.- NAZDAQ, The challenge of extracting data from Baan and Infor LN database. Independent third-party corroboration of the suffix, from a vendor that sells LN reporting tools.
- In Infor M3, Your Custom Fields Are Rows in a Table Called CUGEX1. The sibling product on the same Infor row, with a completely different schema problem.
- Oracle EBS Secured Every Operating Unit in a Session Variable. Your Warehouse Copied the Table.. The same business problem solved with a column instead of a table name, which makes it recoverable in SQL.
- Two Columns, One SAP Material Number, and the Join Uses the Padded One. Another case where the app rendered something the warehouse doesn't.
- Only Your Semantic Model Knows Whether a Workday Student Row Is a Student. The grain set somewhere outside the table you're looking at.
- agami-core on GitHub
Make your Infor LN companies a column
Agami is the trust layer between your AI assistant and your warehouse. It records which physical tables are one logical entity, which company each suffix holds, and which of them are production, so "by company" becomes a question your estate can answer.