Forum Discussion
Are GUIDs a viable substitute for integer Identities/Primary Keys?
Greetings, community. In the world of Fabric data warehouses, there is still no way to truly create int/bigint IDENTITY columns. Some of the proposals I've seen include:
- Use Row_Number() as your IDENTITY/PRIMARY KEY and sequence your jobs in such a way that conflicts aren't created (proposed in Microsoft's own documentation as a workaround).
- Use GUIDs as IDENTITY/PRIMARY KEY.
Regarding #1, there are numerous potential issues this approach poses:
- Uniqueness NOT guaranteed: There is always a risk that the ROW_NUMBER() approach will produce duplicate keys if jobs happen to run too long/overlap OR if a script returns the wrong start value.
Load Process Severely Impacted: This is incredibly limiting when you have numerous data feeds that should be able to run in parallel. This is complicated even more when data load jobs have dependencies on multiple tables (meaning Table C needs a PK from Table B which needs a PK from Table A) as you either have to run source files through one at a time OR build pipelines that hold up other file loads until all other inserts are complete.
That leads me to #2 and using GUIDs to create unique identifiers for use in data ingestion/key assignment jobs. However, I know in the world of DBs that GUIDs have historically been found to be less performant than ints/bigints as IDENTITY/PRIMARY KEY fields and also take up more space. If you have a DB with millions of records, that translates to millions of GUIDs.
My question is: are the performance concerns lessened now that we're in the world of Fabric and Parquet-backed DWs? Have others in the community found GUIDs to be a viable route?
6 Replies
- AndyDDCMost Valuable Professional
Well one consideration is that direct lake isn't supported when using GUIDs so that may affect your choice. I haven't had issues with using rownumber, mostly because I don't do any parallel loading when inserting into dimensions. And with fact tables I am doing parallel loading but they are just looking up existing keys in dim tables.
- arpostPost Prodigy
Thanks for the reply, AndyDDC. Regarding OneLake not being supported, didn't know that! Can you point me to a doc listing that limitation?
Regarding ROW_NUMBER() and parallel load issues, here's a fake example of our parallel loading situation. It's actually even more streamlined than our actual use case will likely end up being.
You have 15 clients sending data for multiple file types with different column structures but similar data:
- Type A: Needs to insert into three tables (Customer, Account, Order) and there are key dependencies (e.g., Account -> Customer, Order -> Account).
- Type B: Needs to insert into three tables (Customer, Account, Payments) and there are key dependencies (e.g., Account -> Customer, Payments-> Account).
For key assignments in Fabric, you'd need to insert into your Customer table, assign a CustomerKey, then insert into your Account table and match to a CustomerKey, assign an AccountKey, and then finish with the insert into Orders or Payments with a match to an AccountKey and assign an OrderKey/PaymentKey.
To achieve this in Fabric with ROW_NUMBER(), you're limited to a few options, none of which are that great:
Option Summary Comment 1. Client Sequential Run each client's files (Types A-B) through before starting next client.
Totally inefficient and makes each client feed AND file type dependent on other clients'. 2. Type Sequential Run each client's Type A files through one-by-one and then start Type B. Similarly inefficient for each client as in #1, but this at least "checks in" Type A data before starting Type B load. 3. Client Interspersed Run all clients' Type A files through, pausing until keys can be assigned, before running Type B.
Requires "pausing" clients' jobs at each key asssignment (CustomerKey, AccountKey, OrderKey/PaymentKey) to make sure no concurrent key assignments take place. A process that you'd normally be able to run fully in parallel now has to get stretched out by several minutes if not hours. Of the choices, #3 is the "best", but that requires a hefty amount of interdependent pipeline logic and planning.