← Microsoft Interview Insights

Microsoft·Data Scientist·Technical Phone Screen·Senior

Senior
Jun 2026

Summary

SQL-heavy technical screen for a Data Scientist role at Microsoft. The whole thing revolved around a single graph problem, building up from a basic reciprocal-edge join to a full common-friends query with deduplication and then index design. Pretty intense for one problem but it tested a lot of ground.

Questions Asked (4)

Q1

Given a directed edge table recording who followed whom, write a single SQL query to produce an undirected friendship table with one row per mutual connection, where the first user column is always less than the second.

Data ModelingAlgorithms & Data Structures
Author's notes

The self-join on reciprocal rows is straightforward enough, but the u1 < u2 constraint to avoid duplicate pairs is the part I almost forgot.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the schema and defining 'mutual connection' as a pair of users who follow each other. Then write a self-join on the edge table to find reciprocal follows, and use a CASE expression or LEAST/GREATEST to order the pair so the smaller user ID comes first. Finally, deduplicate the result to return each friendship once.

Pro tip: Mention that using LEAST and GREATEST is the cleanest way to enforce the ordering, but if the database doesn't support them, a CASE expression works. Also, explicitly state that you'd add a condition like user1 < user2 in the join to avoid duplicate pairs and improve performance.

1. Clarify the schema and requirements

Confirm the table name and columns (e.g., follower_id, followee_id) and define 'mutual connection' as two users following each other. Ask if the output should include only mutual pairs or all friendships.

2. Self-join to find reciprocal follows

Join the edge table to itself on follower_id = followee_id and followee_id = follower_id to find pairs where both directions exist. This identifies mutual connections.

3. Order the user pair

Use LEAST and GREATEST (or a CASE expression) to ensure the first user column is always less than the second. This normalizes the pair and avoids duplicates like (A,B) and (B,A).

4. Deduplicate and select distinct pairs

Apply DISTINCT or GROUP BY to return each friendship only once. Since the self-join may produce two rows per mutual pair (one for each direction), deduplication is essential.

5. Write the final query and test edge cases

Assemble the query and mentally test with sample data, including cases where users follow themselves or where only one direction exists. Ensure the output matches the expected format.

Key Points to Mention

  • Self-join on the edge table to find reciprocal relationships.
  • Use of LEAST/GREATEST or CASE to enforce ordering of user IDs.
  • Deduplication with DISTINCT or GROUP BY to avoid duplicate pairs.
  • Handling of self-follows (e.g., user following themselves) by excluding them.
  • Performance considerations: indexing on follower_id and followee_id, and filtering with user1 < user2 to reduce join size.
  • Clarifying assumptions about the schema and output format before writing the query.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q2

Without using temp tables, write a single SQL query that finds all common friends for every unordered pair of distinct users (x, y) where x < y. A common friend must be mutually connected to both x and y, and the pair itself should not count as its own friend.

Data ModelingAlgorithms & Data StructuresTechnical Trade-offs
Author's notes

This is where things got messy for me.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Use a self-join on the friendship table to find all pairs of users who share a common friend, then filter to ensure the pair members are distinct and ordered (x < y). Exclude cases where the pair members are directly friends with each other, and ensure the common friend is not one of the pair members.

Pro tip: Clarify the schema and assumptions upfront (e.g., friendship is mutual, table name and columns). This shows you think about data modeling and avoids ambiguity, which is crucial in interviews.

1. Clarify schema and assumptions

Ask about the friendship table structure (e.g., columns: user1, user2) and whether friendships are mutual. Confirm that we need unordered pairs with x < y.

2. Self-join to find common friends

Join the friendship table to itself on the common friend column, ensuring that the two users are different and ordered (x < y). This yields candidate pairs (x, y) and their common friend z.

3. Exclude direct friendships and self-pairs

Filter out rows where x and y are directly friends (i.e., there exists a friendship between x and y). Also ensure z is not equal to x or y.

4. Aggregate and output results

Group by x and y, and optionally list the common friends (e.g., using STRING_AGG or array_agg). Ensure the final output contains each unordered pair once.

Key Points to Mention

  • Self-join technique to find mutual connections
  • Filtering conditions: x < y, x != y, z != x, z != y
  • Exclusion of direct friendships between x and y
  • Handling of undirected friendships (if table stores both directions, deduplicate)
  • Aggregation of common friends per pair (if required)
  • Performance considerations: indexing on friend columns, avoiding temp tables

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q3

Extend the previous query to also return the count of distinct common friends per pair. How do you ensure no duplicates appear even if the underlying edge table has redundant reciprocal rows?

Data ModelingTechnical Trade-offs
Author's notes

COUNT(DISTINCT f) wraps it up, but the interviewer pushed on why DISTINCT is necessary.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

First, clarify the schema and whether the edge table contains reciprocal duplicates. Then propose a query that deduplicates edges (e.g., using a canonical ordering or SELECT DISTINCT) before joining to find common friends, and finally count distinct common friends per pair. Emphasize the importance of handling duplicates to avoid inflated counts.

Pro tip: Mention that you would verify the deduplication logic with a quick data audit (e.g., checking for reciprocal rows) and consider using a subquery or CTE to materialize the deduplicated edges for better performance and readability.

1. Clarify the data model

Ask about the edge table structure: are friendships stored as directed edges with possible reciprocal duplicates? Confirm the definition of 'common friends' (e.g., users who are friends with both members of a pair).

2. Deduplicate the edge table

Use a method to ensure each undirected friendship appears once. For example, select rows where user_id < friend_id, or use SELECT DISTINCT with a canonical ordering. This prevents double-counting in the join.

3. Find common friends per pair

Self-join the deduplicated edges to find users who are friends with both members of a pair. Use a join condition that matches the first user to one edge and the second user to another edge, ensuring the common friend is the same.

4. Count distinct common friends

Group by the pair and count distinct common friend IDs. Use COUNT(DISTINCT friend_id) to avoid duplicates if a common friend appears multiple times due to other redundancies.

5. Validate and optimize

Test the query on a small sample to ensure correctness. Consider performance implications and suggest indexing or materializing the deduplicated edges if needed.

Key Points to Mention

  • Deduplication techniques: using canonical ordering (e.g., LEAST/GREATEST or user_id < friend_id) or SELECT DISTINCT.
  • Use of COUNT(DISTINCT) to ensure distinct common friends are counted.
  • Potential performance trade-offs: deduplication may require sorting or hashing, impacting query speed.
  • Handling of self-joins and ensuring the common friend is not one of the pair members.
  • Importance of clarifying the definition of 'common friends' and the edge table schema.
  • Testing with sample data to verify no duplicates and correct counts.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q4

What indexes would you add to the FriendEdges table to make the common-friends query performant at 100 million rows, and why?

System DesignTechnical Trade-offs
Author's notes

I said composite indexes on (user_from, user_to) and (user_to, user_from) to cover both join directions, which felt right.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the common-friends query pattern (e.g., self-join on user_id to find mutual friends) and the table schema. Then propose a composite index on (user_id, friend_id) to support efficient lookups, and discuss whether a covering index or additional indexes on friend_id are needed based on query patterns. Finally, address scalability concerns like index size, write overhead, and potential partitioning strategies for 100M rows.

Pro tip: Mention that at 100M rows, index maintenance and storage become significant, so you'd validate the index with query plans and consider partitioning or denormalization if write throughput is critical. Also, note that Microsoft often values data-driven decisions, so suggest benchmarking with realistic data.

1. Clarify the query and schema

Ask or state assumptions about the common-friends query (e.g., finding friends of friends who are not already friends) and the FriendEdges table structure (e.g., user_id, friend_id, possibly directionality).

2. Identify access patterns

Determine the columns used in JOIN, WHERE, and ORDER BY clauses. For common-friends, this typically involves filtering by user_id and joining on friend_id.

3. Propose indexes

Recommend a composite index on (user_id, friend_id) to quickly find all friends of a user. If the query also filters by friend_id, consider an index on (friend_id, user_id) or a covering index.

4. Evaluate trade-offs

Discuss the impact on write performance, storage, and maintenance. At 100M rows, consider partitioning the table (e.g., by user_id range) and using included columns to make indexes covering.

5. Validate and iterate

Suggest using EXPLAIN plans, query profiling, and A/B testing to confirm the index improves performance without excessive overhead. Mention monitoring index usage and adjusting as needed.

Key Points to Mention

  • Composite index on (user_id, friend_id) for efficient friend lookups
  • Covering index to avoid key lookups if the query selects only indexed columns
  • Consideration of index on (friend_id, user_id) for reverse lookups
  • Impact on write performance and storage at 100M rows
  • Partitioning strategies (e.g., by user_id hash or range) to manage large table
  • Use of EXPLAIN and query profiling to validate index effectiveness

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.