Why a join to China A-share company data silently drops rows
Short answer: because entity resolution is partial by design, the resolved share differs sharply between datasets and even between tables inside one dataset, and an inner join throws away precisely the rows that were not resolved. Those rows are not a random sample. They are the smaller, less covered, harder-to-identify counterparties, so the join does not just shrink your data, it tilts it.
The resolved share is a property of the data, not a defect
A disclosed counterparty in a Chinese filing is a free-text name. It becomes joinable only once it has been matched to a standard company identifier, and matching does not succeed on every row.
| dataset | resolved share | as of |
|---|---|---|
| ChinaScope C2C Disclosed | roughly 28% to 36% of rows carry a resolved ID; the rest are name-only | data cut 2026-05-16 |
| ChinaScope Relationships (PIT) | about 98% on ownership, far lower on guarantees | data cut 2026-08-23 |
The second row is the one that costs people money. Ownership and guarantees sit in the same dataset and resolve at very different rates, so a single inner join across both produces a guarantee network biased toward the largest and best-identified parties, while the ownership network beside it looks fine. The bias is invisible at the row-count level because both queries return plenty of rows.
What to do instead. For graph work on C2C, start from supply_chain_trade,
which is already resolved on both ends. Use the raw line-item tables when you
specifically want the name and share as disclosed. On Relationships, count your
unresolved rows per relationship type before you aggregate, not after.
One key runs through the whole estate
Every company-keyed ChinaScope dataset resolves its entities to the same company identifier, defined in the Reference dataset. SAM, C2C, Affiliate, SmarTag, RFP-Bids and Fundamentals all line up on it. Resolve a company once and its segment revenue, supply-chain edges, ownership graph, news tags, tender awards and filings all join on that one key. That is what makes the datasets composable rather than merely adjacent.
Three ways that key catches people out:
The security ID is not the company ID. To go from a security to its company,
or to any company-keyed dataset, join base_stock.csfid to
base_company.company_id. The security's own id, the S-prefixed one,
identifies the instrument. It is not a company key and will not join like one.
(ChinaScope Reference, as described in the July 2026 profile.)
Company ID prefixes vary. Canonical company identifiers appear under more
than one prefix, CSF… and ICN… among them. Treat the whole string as the
key. Code that assumes a single prefix, or strips one, will drop everything
under the others. (ChinaScope Reference, July 2026 profile.)
In SmarTag, unlisted companies carry a placeholder. 1,914,479 distinct
companies are tagged, and most of them are private or unlisted. Join on
companyid to reach all of them. For listed names, filter stockcode to real
exchange codes and drop the csf placeholder that marks unlisted rows. A-share
codes there match SAM's secu. (ChinaScope SmarTag, data cut 2026-09-16.)
Some tables carry no entity at all until you join
In Fundamentals, the line-item fact carries no dates and no entity. You have to
join it to filing on report_id before it means anything, which is what
attaches publish_date, report_date, company_id, secu, currency and the
schedule. A standardized line appearing under two schedules has two report_id
values with possibly different figures, and keying on report_id is what keeps
them apart. (ChinaScope Fundamentals, data cut 2026-04-14.)
The check worth running first
Before any aggregate, count rows on both sides of the join and count the unresolved. If the drop is more than a percent or two, the question is not "how do I recover them" but "what kind of counterparty am I now missing". On a supply-chain or guarantee network, that answer determines whether the result means anything.