Data Scientists, Analysts, and Data Engineers at American Tower use SQL for analyzing telecommunication tower datasets, and integrating various data sources for predictive maintenance modeling. That's why American Tower asks interviewees SQL coding interview questions.
So, to help you prep, we've curated 9 American Tower SQL interview questions – can you solve them?
American Tower is in the business of leasing space on its communications sites to wireless service providers. Suppose you have been given a task to analyze the monthly revenue they receive from various wireless service providers.
Your task is to find the top 3 service providers that have paid the most for each month of the year 2020.
For this question, assume you have an invoices
table structured as follows:
invoices
Example Input:invoice_id | provider_id | payment_date | amount |
---|---|---|---|
1001 | AT&T | 01/15/2020 | 5000 |
1002 | Verizon | 01/20/2020 | 7000 |
1003 | Sprint | 01/25/2020 | 6000 |
1004 | AT&T | 02/15/2020 | 8000 |
1005 | Verizon | 02/20/2020 | 7500 |
1006 | T-Mobile | 02/25/2020 | 7000 |
1007 | AT&T | 03/15/2020 | 9000 |
1008 | T-Mobile | 03/20/2020 | 9500 |
1009 | Verizon | 03/25/2020 | 8000 |
SELECT date_trunc('month', payment_date) AS month, provider_id, SUM(amount) AS total_payment FROM invoices WHERE date_part('year', payment_date) = 2020 GROUP BY date_trunc('month', payment_date), provider_id QUALIFY row_number() OVER (PARTITION BY date_trunc('month', payment_date) ORDER BY total_payment DESC) <= 3 ORDER BY month, total_payment DESC;
This SQL query should return a result set which shows for each month of the year 2020, the top 3 service providers who made highest payments.
The date_trunc('month', payment_date)
function truncates the date to the start of the month, the WHERE date_part('year', payment_date) = 2020
filters for records in the year 2020, and the QUALIFY row_number() OVER (...) <= 3
statement filters for the top 3 providers.
The GROUP BY
clause groups the records by month and provider_id after which the ORDER BY
clause orders the result set firstly by month and then by total payment in descending order. The OVER
clause creates a window function for each month separately.
Finally row_number() <= 3
filters and keeps only top 3 records of each window.
Pro Tip: Window functions are a frequent SQL interview topic, so practice every window function problem on DataLemur
Fun fact: American towers impacts almost every sector, from everything from Utilities to Sports and Gaming. Read about what industries American Tower works with to learn more.
You're given a table of American Tower employee and department salary information. Write a SQL query to find departments where the departments average salary is higher than the company's average salary.
You can solve this interview question directly within the browser on DataLemur:
The solution is LONG – 30+ lines of SQL. You can find a step-by-step solution with hints here: Department Salaries.
RANK()
and DENSE_RANK()
differ when it comes to ranking rows in a result set?While both RANK()
and DENSE_RANK()
are used to rank rows, the key difference is in how they deal with ties.
RANK(): When there's a tie, RANK()
leaves a gap in the ranking. For example, if three rows are tied for 2nd place, the RANK() function will assign a rank of 2 to the first of these rows, a rank of 3 to the 2nd row in the tie, and a rank of 4 to the the 3rd tie.
DENSE_RANK(): For ties, DENSE_RANK()
does not leave a gap in the ranking. Instead, it assigns the same rank to all tied rows, and then makes the next row 1 bigger. Confusing, I know, but here's an example to make it more clear: if three rows are tied for 3rd place, the DENSE_RANK()
function will assign a rank of 3 to all three rows, and then assign a rank of 4 to the next row.
Suppose we had data on how many deals different salespeople at American Tower closed, and wanted to rank the salespeople.
WITH data AS ( SELECT 'Akash' AS name, 50 AS deals_closed UNION ALL SELECT 'Brittany', 50 UNION ALL SELECT 'Carlos', 40 UNION ALL SELECT 'Dave', 30 UNION ALL SELECT 'Eve', 30 UNION ALL SELECT 'Frank', 20 ) SELECT name, deals_closed, RANK() OVER (ORDER BY deals_closed DESC) as rank, DENSE_RANK() OVER (ORDER BY deals_closed DESC) as dense_rank FROM american_tower_sales;
The result of this query would be:
name | deals_closed | rank | dense_rank |
---|---|---|---|
Akash | 50 | 1 | 1 |
Brittany | 50 | 2 | 1 |
Carlos | 40 | 3 | 2 |
Dave | 40 | 4 | 3 |
Eve | 30 | 5 | 3 |
Frank | 20 | 6 | 4 |
As you can see, the RANK()
function assigns a rank of 1 to the first row (Akash), a rank of 2 to the second row (Brittany), and so on. In contrast, the DENSE_RANK()
, gives both Akash and Brittany a rank of 1, since they are tied, then moves on to rank 2 for Carlos.
American Tower Corporation is a real estate investment trust and an independent owner, operator, and developer of multitenant communications real estate. The company rents space on communications towers that it owns or operates to wireless service providers, radio and television broadcast companies, wireless data providers, government agencies and municipalities, and tenants in several other industries.
Your task is to write a SQL query that receives the customer_id
, rent_start_date
, rent_end_date
, tower_id
, contract_status
as inputs. The goal is to filter the records of customers who started renting between 2020 and 2022 and whose contracts are active. Additionally, we are only interested in towers located in "New York" and "California".
Let's assume our databases customers
and towers
look like this:
customers
Example Input:customer_id | rent_start_date | rent_end_date | tower_id | contract_status |
---|---|---|---|---|
112 | 01/07/2020 | 01/07/2023 | 2001 | Active |
245 | 13/09/2018 | 13/09/2021 | 5048 | Finished |
389 | 06/12/2021 | 06/12/2024 | 1098 | Active |
980 | 15/03/2019 | 15/03/2022 | 3350 | Finished |
651 | 21/06/2022 | 21/06/2023 | 2001 | Active |
towers
Example Input:tower_id | tower_location |
---|---|
2001 | New York |
5048 | Massachusetts |
1098 | California |
3350 | Texas |
Using PostgreSQL, we could structure a query like this:
SELECT c.customer_id, c.rent_start_date, c.rent_end_date, c.tower_id, c.contract_status FROM customers c INNER JOIN towers t ON c.tower_id = t.tower_id WHERE c.rent_start_date BETWEEN '2020-01-01' AND '2022-12-31' AND c.contract_status = 'Active' AND t.tower_location IN ('New York', 'California');
In this query, we join the customers
and towers
tables on the tower_id
column, then we apply our filters in the WHERE
clause. We consider only the rows where rent_start_date
is between 2020 and 2022, if contract_status
is 'Active' and if the tower_location
is New York or California.
This way, we end up with the data of the customers that satisfy all these conditions.
A correlated sub-query is a sub-query that depends on the outer query and cannot be executed separately. It uses the outer query to filter or transform data by referencing a column from it, and the outer query uses the results of the inner query. In contrast, a non-correlated sub-query is independent of the outer query and can be executed on its own. It does not reference any columns from the outer query and is used to retrieve additional data needed by the outer query.
Here is an example of a correlated sub-query:
SELECT t1.customer_id, t1.total_sales FROM american_tower_sales t1 WHERE t1.total_sales > ( SELECT AVG(t2.total_sales) FROM american_tower_sales t2 WHERE t2.customer_id = t1.customer_id );
This query selects the customer_id
and total sales of all American Tower customers in the sales table whose total_sales
are greater than the average total_sales
of their own customer group. The sub-query in this case is correlated with the outer query, as it references the customer_id
column from the outer query (t1.customer_id
).
Here is an example of a non-correlated sub-query:
SELECT t1.customer_id, t1.total_sales FROM american_tower_sales t1 WHERE t1.total_sales > ( SELECT AVG(t2.total_sales) FROM american_tower_sales t2 );
The sub-query in this case is non-correlated, as it does not reference any columns from the outer query.
Performance-wise, correlated sub-queries are generally slower to execute, as they have to be re-evaluated for each row of the outer query, while non-correlated sub-queries are faster, as they only have to be executed once.
At "American Tower", a real estate company that leases spaces on their towers to telecommunication companies,every leasing contract has a rental duration. Can you find the average rental duration for each tower?
Below is some sample data in markdown-formatted tables.
tower_rentals
Example Input:rental_id | tower_id | tenant_id | start_date | end_date |
---|---|---|---|---|
1250 | 432 | 296 | 01/02/2019 | 01/04/2019 |
9375 | 864 | 725 | 01/03/2019 | 01/07/2019 |
1208 | 432 | 530 | 01/06/2019 | 01/10/2019 |
4671 | 864 | 192 | 01/02/2019 | 01/03/2019 |
7846 | 950 | 981 | 01/01/2019 | 01/10/2019 |
tower_id | avg_duration |
---|---|
432 | 4.00 |
864 | 3.50 |
950 | 9.00 |
SELECT tower_id, AVG(end_date - start_date) AS avg_duration FROM tower_rentals GROUP BY tower_id
This SQL query uses the AVG() function to calculate the average rental duration per tower. The duration of each rental is found by subtracting the start_date
from the end_date
. All towers are included into separate groups using GROUP BY on tower_id
for the average calculation.
To practice a very similar question try this interactive Facebook Average Post Hiatus (Part 1) Question which is similar for calculating duration between two dates or this Amazon Average Review Ratings Question which is similar for working with averages.
FOREIGN KEY
constraint?A FOREIGN KEY
is a column or set of columns in a table that references the primary key of another table. It is used to link the two tables together, and to ensure that the data in the foreign key column is valid.
The FOREIGN KEY
constraint helps to ensure the integrity of the data in the database by preventing the insertion of rows in the foreign key table that do not have corresponding entries in the primary key table. It also helps to enforce the relationship between the two tables, and can be used to ensure that data is not deleted from the primary key table if there are still references to it in the foreign key table.
For example, if you have a table of American Tower customers and an orders table, the customer_id column in the orders table could be a FOREIGN KEY
that references the id column (which is the primary key) in the American Tower customers table.
American Tower rents space on its vast network of multi-tenant communications sites to broadcasters, wireless service providers, and broadband providers. They want to drive more potential customers to sign up for their services from their website.
As a data analyst, you are asked to calculate the click-through rate (CTR) to measure how successful the company's digital ad campaigns and check the conversion rate from ad click to signing up for the company's services. Given the following tables:
ad_clicks
Example Input:ad_id | user_id | time_clicked |
---|---|---|
1 | 123 | 06/08/2022 00:00:00 |
2 | 265 | 06/10/2022 00:00:00 |
3 | 362 | 06/18/2022 00:00:00 |
4 | 192 | 07/26/2022 00:00:00 |
5 | 981 | 07/05/2022 00:00:00 |
sign_ups
Example Input:user_id | signup_time |
---|---|
123 | 06/09/2022 00:00:00 |
265 | 06/10/2022 00:00:00 |
192 | 07/25/2022 00:00:00 |
Calculate the click-through rate and the conversion rate per day.
WITH daily_clicks AS ( SELECT DATE(time_clicked) AS day, COUNT(*) AS num_clicks FROM ad_clicks GROUP BY day ), daily_sign_ups AS ( SELECT DATE(signup_time) AS day, COUNT(*) AS num_signups FROM sign_ups GROUP BY day ), combined AS ( SELECT COALESCE(daily_clicks.day, daily_sign_ups.day) AS day, COALESCE(num_clicks, 0) AS num_clicks, COALESCE(num_signups, 0) AS num_signups FROM daily_clicks FULL JOIN daily_sign_ups ON daily_clicks.day = daily_sign_ups.day ) SELECT day, num_clicks, num_signups, num_signups::float / NULLIF(num_clicks::float, 0) AS conversion_rate FROM combined;
The above query first groups the ad clicks and the sign-ups by day. Then it uses a full join to combine the two tables into one, using COALESCE to fill any missing values with 0. Finally, it calculates the conversion rate as the ratio of sign-ups to clicks for each day.
To solve a similar SQL problem on DataLemur's free interactive SQL code editor, solve this Facebook SQL Interview question:
American Tower is a company in the real estate sector that specializes in owning and operating cellular towers.The company wants to be able to calculate the efficiency of each of its towers. Efficiency in this context is a ratio of current power usage to maximum power capacity, with values closer to 1 indicating a more efficient use of the structure. The company has a table of towers, each with an ID, location, current power usage, and maximum power capacity, and another table with financial data including costs and revenues for each tower. Use the abs()
function to find the absolute difference between costs and revenues, round()
to round the efficiency ratio to two decimal digits, and the arithmetic operators to calculate the efficiency and profitability of each tower.
towers
Example Input:tower_id | location | current_power_usage | maximum_power_capacity |
---|---|---|---|
1001 | Boston | 15000 | 20000 |
1002 | Chicago | 18000 | 20000 |
1003 | Seattle | 12000 | 20000 |
1004 | Denver | 16000 | 20000 |
finance
Example Input:tower_id | costs | revenues |
---|---|---|
1001 | 120000 | 150000 |
1002 | 100000 | 90000 |
1003 | 80000 | 90000 |
1004 | 150000 | 160000 |
We want to find the efficiency for each tower and the profitability based on costs and revenues.
tower_id | location | efficiency | profitability |
---|---|---|---|
1001 | Boston | 0.75 | 30000 |
1002 | Chicago | 0.90 | -10000 |
1003 | Seattle | 0.60 | 10000 |
1004 | Denver | 0.80 | 10000 |
SELECT t.tower_id, t.location, ROUND((t.current_power_usage::numeric / t.maximum_power_capacity), 2) AS efficiency, ABS(f.costs - f.revenues) as profitability FROM towers t JOIN finance f ON t.tower_id = f.tower_id;
This query first calculates the ratio of the current power usage to the maximum power capacity using division (/
). It then rounds this ratio to two decimal places using the ROUND()
function. The ABS()
function is used to calculate the absolute difference between costs and revenues, providing the profitability value for each tower. Finally, the JOIN
operation merges the towers table and the financial table based on the tower ID, producing a combined set of results for each tower.
To practice a very similar question try this interactive Amazon Server Utilization Time Question which is similar for calculating usage efficiency or this Google Odd and Even Measurements Question which is similar for handling efficiency related calculations.
The best way to prepare for a American Tower SQL interview is to practice, practice, practice.
Beyond just solving the earlier American Tower SQL interview questions, you should also solve the 200+ FAANG SQL Questions on DataLemur which come from companies like FAANG (FB, Apple, Amazon, Netflix, Google).
Each DataLemur SQL question has hints to guide you, step-by-step solutions and best of all, there is an online SQL coding environment so you can right online code up your SQL query answer and have it executed.
To prep for the American Tower SQL interview it is also useful to practice SQL problems from other telecom and REIT companies like:
However, if your SQL query skills are weak, don't worry about diving straight into solving questions – refresh your SQL knowledge with this DataLemur SQL Tutorial.
This tutorial covers SQL concepts such as window functions and transforming strings with CONCAT()/LOWER()/TRIM() – both of which pop up often in American Tower SQL interviews.
In addition to SQL query questions, the other topics covered in the American Tower Data Science Interview include:
I'm sort of biased, but I believe the optimal way to study for American Tower Data Science interviews is to read the book Ace the Data Science Interview.
The book solves 201 interview questions taken from Facebook, Google & startups. It also has a crash course on Python, SQL & ML. And finally it's helped thousands of people land their dream job in data, which is why it's earned 1000+ reviews on Amazon and a 4.5-star rating.