DEV Community

Cover image for Part 2: Zero-Copy data federation Snowflake Customer 360 Data to Salesforce Sales Reps
Krishna Tangudu
Krishna Tangudu

Posted on Edited on

Part 2: Zero-Copy data federation Snowflake Customer 360 Data to Salesforce Sales Reps

In Part 1, we connected Salesforce Data Cloud to Snowflake for analytics. In this blog, you'll flip the direction: surface your Snowflake Customer 360 model (Orders, Service contracts, invoices, Repairs, etc.) directly into Salesforce so your sales and service teams can activate it in real time—without ETL or data duplication.

Snowflake ↔ Salesforce Data Cloud ↔ Salesforce CRM

Snowflake: Owns Customer 360 models, health scores, churn risk

Zero-Copy: Data Cloud queries Snowflake live (pushdown SQL)

Salesforce: Activates signals (flows, UI, campaigns)

Architecture Pattern

  • Your data team builds 'CUSTOMER_360_VIEW' with 'ACCOUNT_ID', 'HEALTH_SCORE', 'CHURN_RISK', 'NEXT_BEST_PRODUCT'
  • Modeled once, governed in Snowflake (roles, masking, row access)
  • Updated via your ELT pipelines (daily/hourly)

Step 1: Building data model in Snowflake


-- ============================================================
-- COMPLETE DEMO SCRIPT: Accounts + Customer 360 + Final Table
-- ============================================================

-- Optional: set your context
-- USE ROLE <your_role>;
-- USE WAREHOUSE <your_warehouse>;
-- USE DATABASE <your_database>;
-- USE SCHEMA <your_schema>;

-- ------------------------------------------------------------
-- 1) Clean up old objects (safe for reruns)
-- ------------------------------------------------------------
DROP TABLE IF EXISTS ACCOUNT_C360_SNAPSHOT;
DROP TABLE IF EXISTS CUSTOMER_360_METRICS;
DROP TABLE IF EXISTS ACCOUNTS;

-- ------------------------------------------------------------
-- 2) Create source tables
-- ------------------------------------------------------------
CREATE OR REPLACE TABLE ACCOUNTS (
    ACCOUNT_ID NUMBER,
    ACCOUNT_NAME STRING,
    INDUSTRY STRING,
    ANNUAL_REVENUE NUMBER(18,2),
    DELETED_AT TIMESTAMP_NTZ
);

CREATE OR REPLACE TABLE CUSTOMER_360_METRICS (
    ACCOUNT_ID NUMBER,
    HEALTH_SCORE FLOAT,
    CHURN_RISK FLOAT,
    NPS_SCORE NUMBER(5,2),
    ARR NUMBER(18,2),
    NEXT_BEST_PRODUCT STRING,
    LAST_ENGAGEMENT_DATE DATE
);

-- ------------------------------------------------------------
-- 3) Insert sample data (toy companies)
-- ------------------------------------------------------------
INSERT INTO ACCOUNTS (ACCOUNT_ID, ACCOUNT_NAME, INDUSTRY, ANNUAL_REVENUE, DELETED_AT) VALUES
(1001, 'Bright Blocks Toys',  'Toys', 25000000, NULL),
(1002, 'Rocket Wheels Co',    'Toys',  8000000, NULL),
(1003, 'Cuddle Critters Inc', 'Toys', 12000000, NULL),
(1004, 'Puzzle Planet Ltd',   'Toys', 30000000, NULL),
(1005, 'Old Toy Factory',     'Toys',  5000000, '2025-01-15 10:00:00'),
(NULL, 'Bad Row',             'Toys',   100000, NULL);

INSERT INTO CUSTOMER_360_METRICS (
    ACCOUNT_ID, HEALTH_SCORE, CHURN_RISK, NPS_SCORE, ARR, NEXT_BEST_PRODUCT, LAST_ENGAGEMENT_DATE
) VALUES
(1001, 0.91, 0.22, 60, 1800000, 'STEM Robot Kit',           '2026-03-10'),
(1002, 0.67, 0.45, 35,  650000, 'Turbo Track Racing Set',   '2026-03-05'),
(1003, 0.43, 0.81, 10,  420000, 'Deluxe Plush Care Pack',   '2026-02-20'),
(1004, 0.80, 0.70, 55, 2200000, 'Master Puzzle Collection', '2026-03-14'),
(9999, 0.95, 0.10, 70,  900000, 'Collector Train Set',      '2026-03-01'); -- no matching account

-- ------------------------------------------------------------
-- 4) Create final table using your view definition logic
-- ------------------------------------------------------------
CREATE OR REPLACE TABLE ACCOUNT_C360_SNAPSHOT AS
SELECT
    A.ACCOUNT_ID,
    UPPER(A.ACCOUNT_NAME) AS ACCOUNT_NAME,
    A.INDUSTRY,
    A.ANNUAL_REVENUE,
    C360.HEALTH_SCORE,
    C360.CHURN_RISK,
    C360.NPS_SCORE,
    C360.ARR,
    C360.NEXT_BEST_PRODUCT,
    C360.LAST_ENGAGEMENT_DATE,
    CASE
        WHEN C360.HEALTH_SCORE >= 0.8 THEN 'Healthy'
        WHEN C360.HEALTH_SCORE >= 0.5 THEN 'At Risk'
        ELSE 'Critical'
    END AS HEALTH_STATUS,
    CASE
        WHEN C360.CHURN_RISK >= 0.7 THEN 'High'
        WHEN C360.CHURN_RISK >= 0.4 THEN 'Medium'
        ELSE 'Low'
    END AS CHURN_RISK_LEVEL,
    CURRENT_TIMESTAMP() AS LAST_UPDATED
FROM ACCOUNTS A
INNER JOIN CUSTOMER_360_METRICS C360
    ON A.ACCOUNT_ID = C360.ACCOUNT_ID
WHERE A.ACCOUNT_ID IS NOT NULL
  AND A.DELETED_AT IS NULL;


Enter fullscreen mode Exit fullscreen mode

Data Cloud keeps a **virtual reference to Snowflake views—no data duplication, always fresh**

Step 2: Connecting Salesforce Data Cloud to Snowflake using zero copy data federation

First, review your network policies and ensure that Salesforce Data Cloud IPs are included.

Salesforce Data Cloud connection:

Note: Make sure the private key is unencrypted and eliminate comments, such as "BEGINNING OF PRIVATE KEY" and "END OF PRIVATE KEY".

Create a Snowflake Data Stream in Data Cloud:

Select the required object from Snowflake:

Configure with below details:

Field Value Notes
Object Name Customer_360 Simple, no special chars; this is the internal name Data Cloud uses
Category Accounts Helps organize data streams by business domain
Primary Key ACCOUNT_ID The unique identifier that joins to Salesforce Account records
Organization Unit Identifier ACCOUNT_ID (Optional; keep same as Primary Key unless you have a multi-tenant model)

Choose Live/Schedule:

Enable Select Acceleration only when necessary.
This approach allows Salesforce to cache data on a scheduled basis instead of querying Snowflake for every request.
When users attempt to load large volumes of data but only select a small subset of columns, querying Snowflake directly can become unnecessarily expensive. Leveraging Select Acceleration in these scenarios helps reduce compute costs and improves query performance by avoiding repeated full-table scans.

Step 3. Salesforce as Activation Layer

Sales reps see Snowflake signals in Account/Opportunity pages, flows create tasks automatically

Maps ACCOUNT_ID → Data Cloud profile, pulls HEALTH_SCORE, CHURN_RISK

Live signals drive real-time actions without swivel-chair between BI and CRM.

Monitoring:

Queries fired by Salesforce can be monitored in snowflake in Query History:

Governance & Ownership

Snowflake: Data ownership, model logic, calculations
Data Cloud: Unification, activation logic
Salesforce: UX, workflows, business rules

Key patterns from production:

  • Snowflake auto-scaling handles Data Cloud query bursts
  • Explicit 'GRANT SELECT' on views (not tables) for least privilege
  • Data Cloud refresh schedule matches Snowflake pipeline cadence

Production Impact

Before: Sellers export CSV from Snowflake → manual Salesforce upload
After: Zero-copy signals → automatic tasks → faster CSM response

What's next?

Salesforce Data Cloud also provides an option for file‑based federation when using the Snowflake Polaris Catalog Account.

This enables data access without continuously querying Snowflake, offering an alternative integration pattern for specific use cases.

Whether this capability will be extended to the Horizon Catalog remains to be seen—something to watch as the platform evolves.

Top comments (1)

Collapse
 
andrei_iunisov profile image
Andrei Iunisov •

Really clear walkthrough of the reverse direction.

The Snowflake → Data Cloud → Salesforce activation pattern is exactly where a lot of teams get stuck after they finish the analytics side. One practical note that has helped in similar setups: be deliberate about when you enable Select Acceleration. Live query is great for low-volume, high-freshness signals, but once reps or flows start hitting larger result sets the Snowflake compute cost can climb quickly.
Matching the Data Cloud refresh schedule to the underlying ELT cadence (and keeping the federated object narrow) usually keeps both cost and latency predictable.

Also appreciated the explicit GRANT SELECT on the view and the soft-delete filter in the sample.One related operational layer we see after activation is in place: the Salesforce-side outcomes (tasks created by flows, health-status fields written to Accounts, related activity history) still need durable, customer-owned retention and restore. I work with GRAX on the product side: teams use it to keep a continuous, queryable backup of those Salesforce objects in their own cloud so the activation history remains recoverable even after org changes or retention policies clear older records. Native Data Export remains the baseline for a one-time snapshot; the continuous approach just closes the gap for teams that need ongoing auditability of the CRM actions the signals trigger.

Thanks for publishing the full demo script.