You are asked to design a simplified SQL engine. The engine should be able to create tables, insert rows, delete rows, and select rows based on simple conditions.
Implement the SQL class:
SQL(): Initializes the SQL engine. All tables are initially empty.void createTable(string tableName, vector<string> columns): Creates a new table named tableName with the given columns. All columns store integer values.void insertRow(string tableName, vector<string> row): Inserts a new row into the specified tableName. The values are provided as a vector of strings. The number of values will match the number of columns, and the values are valid integer strings.void deleteRows(string tableName, string whereColumn, string whereValue): Deletes all rows from tableName where the value in the whereColumn is equal to whereValue. You can assume whereColumn exists and whereValue is a valid integer string.vector<vector<string>> selectRows(string tableName, vector<string> columns, string whereColumn, string whereValue): Selects and returns a list of rows from tableName. Only rows where the value in whereColumn is equal to whereValue should be included. The returned rows should only contain the columns specified in the columns vector, in the same order.Example 1:
SQL sql = new SQL();
sql.createTable("Employees", {"employee_id", "name", "salary"});
sql.insertRow("Employees", {"1", "Alice", "100000"});
sql.insertRow("Employees", {"2", "Bob", "90000"});
sql.insertRow("Employees", {"3", "Charlie", "80000"});
sql.deleteRows("Employees", "employee_id", "3");
sql.selectRows("Employees", {"name", "salary"}, "salary", "100000");
// returns [["Alice", "100000"]]
sql.createTable("Projects", {"project_id", "project_name"});
sql.insertRow("Projects", {"101", "Alpha"});
sql.insertRow("Projects", {"102", "Beta"});
sql.deleteRows("Projects", "project_id", "101");
sql.selectRows("Projects", {"project_name"}, "project_name", "Beta");
// returns [["Beta"]]
Constraints:
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force method for designing SQL involves exploring every conceivable table structure and query construction. We essentially try out every single possible database design, evaluate it, and compare it against our requirements. It's like testing every lock to find the right key.
Here's how the algorithm would work step-by-step:
class SQL:
def __init__(self, names, columns):
# Maps each table name to its column count, rows and next row id.
self.column_counts = dict(zip(names, columns))
self.rows = {name: {} for name in names}
self.next_row_ids = {name: 1 for name in names}
def insertRow(self, name, row):
row_id = self.next_row_ids[name]
self.rows[name][row_id] = row
self.next_row_ids[name] = row_id + 1
def deleteRow(self, name, rowId):
# Row ids are never reused, so removing the entry is enough.
self.rows[name].pop(rowId, None)
def selectCell(self, name, rowId, columnId):
return self.rows[name][rowId][columnId - 1]
Designing a database effectively involves understanding the relationships between different pieces of information. The optimal approach centers around breaking down complex data into smaller, manageable parts and defining clear connections between them.
Here's how the algorithm would work step-by-step:
class SQLDesign:
def __init__(self):
pass
def design_database(self):
# Define tables as dictionaries.
tables = {}
# Create the customers table.
tables['customers'] = {
'customer_id': 'INT PRIMARY KEY',
'customer_name': 'VARCHAR(255)',
'customer_email': 'VARCHAR(255)'
}
# Create the orders table.
tables['orders'] = {
'order_id': 'INT PRIMARY KEY',
'customer_id': 'INT',
'order_date': 'DATE',
'total_amount': 'DECIMAL(10, 2)'
}
# Establish foreign key relationship.
# Orders table relates to customers table using customer_id.
tables['orders']['customer_id'] = 'INT FOREIGN KEY REFERENCES customers(customer_id)'
tables['products'] = {
'product_id': 'INT PRIMARY KEY',
'product_name': 'VARCHAR(255)',
'product_description': 'TEXT',
'product_price': 'DECIMAL(10, 2)'
}
tables['order_items'] = {
'order_item_id': 'INT PRIMARY KEY',
'order_id': 'INT',
'product_id': 'INT',
'quantity': 'INT',
'item_price': 'DECIMAL(10, 2)'
}
# Establish foreign key relationships.
# Order items relate to orders and products tables.
tables['order_items']['order_id'] = 'INT FOREIGN KEY REFERENCES orders(order_id)'
# Products need to be linked for item details
tables['order_items']['product_id'] = 'INT FOREIGN KEY REFERENCES products(product_id)'
# Returns the data model as a dictionary.
return tables| Case | How to Handle |
|---|---|
| CREATE TABLE with duplicate column names | Reject the CREATE TABLE statement if duplicate column names are detected. |
| INSERT INTO a table that doesn't exist | Return an error indicating the table does not exist. |
| INSERT INTO with incorrect number of values (too few or too many) | Return an error if the number of provided values doesn't match the table schema. |
| SELECT * FROM non-existent table | Return an error indicating that the table does not exist. |
| WHERE clause with invalid column name | Return an error if the column specified in the WHERE clause does not exist in the table. |
| WHERE clause with mismatched data types for comparison | Return an error if data types used in the comparison within the WHERE clause are incompatible. |
| Large number of rows in a table causing memory issues | Consider implementing pagination or limiting the size of results returned by SELECT. |
| Complex WHERE clauses with nested AND/OR operators and precedence | Implement a proper parser to handle operator precedence and ensure correct evaluation of the WHERE clause conditions. |