diff --git a/README.md b/README.md index 608c120..3d9e642 100644 --- a/README.md +++ b/README.md @@ -60,28 +60,43 @@ ON s.product_id = p.product_id [1581 - Customer Who Visited but Did Not Make Any Transactions](https://leetcode.com/problems/customer-who-visited-but-did-not-make-any-transactions/) ```sql -SELECT customer_id, COUNT(*) as count_no_trans -FROM Visits -WHERE visit_id NOT IN (SELECT DISTINCT visit_id FROM Transactions) -GROUP BY customer_id +select v.customer_id, count(v.visit_id) as count_no_trans +from Visits v +left join Transactions t +On v.visit_id = t.visit_id +Where t.transaction_id is Null +group by v.customer_id; + +--2nd approach: using Subquery (This approach is not ideal as this problem can be solved using joins) +SELECT customer_id, COUNT(visit_id) as count_no_trans +FROM Visits +WHERE visit_id NOT IN ( + SELECT visit_id FROM Transactions + ) +GROUP BY customer_id; ``` [197 - Rising Temperature](https://leetcode.com/problems/rising-temperature/) ```sql -SELECT w1.id -FROM Weather w1, Weather w2 -WHERE DATEDIFF(w1.recordDate, w2.recordDate) = 1 -AND w1.temperature > w2.temperature +SELECT + w1.id +FROM + Weather w1 +JOIN + Weather w2 +ON + DATEDIFF(w1.recordDate, w2.recordDate) = 1 +WHERE + w1.temperature > w2.temperature; --- OR -SELECT w1.id -FROM Weather w1, Weather w2 -WHERE w1.temperature > w2.temperature -AND SUBDATE(w1.recordDate, 1) = w2.recordDate +-- OR We can use lag() window function to get previous_temperature and record_date columns beside our table and filter it ``` + [1661 - Average Time of Process per Machine](https://leetcode.com/problems/average-time-of-process-per-machine/) ```sql + +-- 1ST approach is by writing subquery to make temporary table first and then we apply math function SELECT machine_id, ROUND(AVG(end - start), 3) AS processing_time FROM (SELECT machine_id, process_id, @@ -90,28 +105,41 @@ FROM FROM Activity GROUP BY machine_id, process_id) AS subq GROUP BY machine_id + +-- OR this is another approach but this put math expession in one go +SELECT + machine_id, + round(SUM(CASE WHEN activity_type='start' THEN timestamp*-1 ELSE timestamp END) + / (SELECT COUNT(DISTINCT process_id)),3) AS processing_time +FROM + Activity +GROUP BY machine_id; + ``` [577 - Employee Bonus](https://leetcode.com/problems/employee-bonus/solutions/) ```sql +-- Note that e.name b.bonus can also be written in select statement SELECT name, bonus FROM Employee e LEFT JOIN Bonus b ON e.empId = b.empId -WHERE bonus < 1000 -OR bonus IS NULL +WHERE bonus < 1000 OR bonus IS NULL ``` [1280 - Students and Examinations](https://leetcode.com/problems/students-and-examinations/) ```sql -SELECT a.student_id, a.student_name, b.subject_name, COUNT(c.subject_name) AS attended_exams -FROM Students a -JOIN Subjects b -LEFT JOIN Examinations c -ON a.student_id = c.student_id -AND b.subject_name = c.subject_name -GROUP BY 1, 3 -ORDER BY 1, 3 +-- Beautiful and brain storming one cross join is must + +select st.student_id, st.student_name, s.subject_name, +count(e.student_id) as attended_exams + +from Students st +cross join Subjects s +left join Examinations e +On st.student_id = e.student_id and s.subject_name = e.subject_name +group by 1,2,3 +order by 1,2,3 ``` [570. Managers with at Least 5 Direct Reports](https://leetcode.com/problems/managers-with-at-least-5-direct-reports) ```sql @@ -124,13 +152,6 @@ WHERE id IN HAVING COUNT(*) >= 5 ) --- OR -SELECT a.name -FROM Employee a -JOIN Employee b -WHERE a.id = b.managerId -GROUP BY b.managerId -HAVING COUNT(*) >= 5 ``` [1934. Confirmation Rate](https://leetcode.com/problems/confirmation-rate/) @@ -147,6 +168,8 @@ FROM Signups s LEFT JOIN Confirmations c ON s.user_id = c.user_id GROUP BY s.user_id; + + ``` [620. Not Boring Movies](https://leetcode.com/problems/not-boring-movies) @@ -157,18 +180,29 @@ FROM Cinema WHERE id % 2 <> 0 AND description <> "boring" ORDER BY rating DESC + +-- To reduce run time, best way +select id, movie, description, rating +from Cinema +where id%2 !=0 and description != "boring" +order by rating desc ``` [1251. Average Selling Price](https://leetcode.com/problems/average-selling-price/) ```sql --- avg(selling), round 2 -SELECT p.product_id, - ROUND(SUM(price * units) / SUM(units), 2) AS average_price -FROM Prices p -LEFT JOIN UnitsSold s -ON p.product_id = s.product_id -AND purchase_date BETWEEN start_date AND end_date -GROUP BY p.product_id +-- avg(selling), round 2 +-- I solved this problem by creating cte first with price*units and continued writing but that takes 3370 ms. +-- This code took only 835 ms. note that try to avoid CTEs if possible it takes more time for code to run + +select p.product_id, + round(coalesce(sum(units * price)/sum(units),0),2) as average_price + + from Prices p + left join UnitsSold u + On p.product_id = u.product_id and + purchase_date between start_date and end_date + group by p.product_id + ``` [1075. Project Employees I](https://leetcode.com/problems/project-employees-i) @@ -183,6 +217,7 @@ GROUP BY project_id [1633. Percentage of Users Attended a Contest](https://leetcode.com/problems/percentage-of-users-attended-a-contest) ```sql +-- Common sense problem. don't use joins. as denominator is same for each contest_id group, use subquery very simple -- % desc, contest_id asc, round 2 SELECT r.contest_id, ROUND(COUNT(DISTINCT r.user_id) * 100 / (SELECT COUNT(DISTINCT user_id) FROM Users), 2) AS percentage @@ -202,6 +237,9 @@ FROM Queries GROUP BY query_name -- OR +-- This is nice approach. poor rating is defined in question as less than 3 rating so we need to count +-- how many got less than 3/total ratings and then get percentage + SELECT query_name, ROUND(AVG(rating/position), 2) AS quality, ROUND(SUM( @@ -234,6 +272,9 @@ GROUP BY 1, 2 [1174. Immediate Food Delivery II](https://leetcode.com/problems/immediate-food-delivery-ii/) ```sql +-- subquery in the WHERE clause, which gets the minimum order date (MIN(order_date)) for each customer. +-- This means considering first order placed by a customer. + SELECT ROUND((COUNT(CASE WHEN d.order_date = d.customer_pref_delivery_date THEN 1 END) / COUNT(*)) * 100, 2) immediate_percentage FROM Delivery d @@ -250,10 +291,33 @@ FROM ( SELECT *, RANK() OVER(partition by customer_id ORDER BY order_date) od FROM Delivery) temp WHERE temp.od = 1 + +-- cte approach + +With first_orders as (select customer_id, order_date, customer_pref_delivery_date +from Delivery +where (customer_id, order_date) IN ( + select customer_id, min(order_date) + from Delivery + group by customer_id ) + +) + +select round(SUM(order_date = customer_pref_delivery_date)*100/ COUNT(*),2) + AS immediate_percentage + from first_orders + ``` [550. Game Play Analysis IV](https://leetcode.com/problems/game-play-analysis-iv/) ```sql + +-- Visual Flow of Logic: +-- Login Date: Find the first login date of each player, Recent Login: Add 1 day to the first login to define the "next day." +-- Count of Players Logging in on the Next Day: Count how many players logged in on that "next day." +-- Calculate Fraction: Divide that count by the total number of distinct players and round the result. +-- The absence of a FROM clause after SELECT ROUND(...) AS fraction is valid because the CTEs and subqueries are already providing the necessary data. + WITH login_date AS (SELECT player_id, MIN(event_date) AS first_login FROM Activity GROUP BY player_id), @@ -266,6 +330,28 @@ SELECT ROUND((SELECT COUNT(DISTINCT(player_id)) FROM Activity WHERE (player_id, event_date) IN (SELECT player_id, next_day FROM recent_login)) / (SELECT COUNT(DISTINCT player_id) FROM Activity), 2) AS fraction + +--or second logic: we subtract 1 day from the player's event_date to see if it matches the player's first login date. + +SELECT + ROUND( + COUNT(A1.player_id) + / (SELECT COUNT(DISTINCT A3.player_id) FROM Activity A3) + , 2) AS fraction +FROM + Activity A1 +WHERE + (A1.player_id, DATE_SUB(A1.event_date, INTERVAL 1 DAY)) IN ( + SELECT + A2.player_id, + MIN(A2.event_date) + FROM + Activity A2 + GROUP BY + A2.player_id + ); + + ``` [2356. Number of Unique Subjects Taught by Each Teacher](https://leetcode.com/problems/number-of-unique-subjects-taught-by-each-teacher) ```sql @@ -335,6 +421,16 @@ SELECT COALESCE( ORDER BY num DESC LIMIT 1), null) AS num + +-- OR CTE APPROACH + +With cte as (select num, count(num) as single_occurance + from MyNumbers + group by num + having count(num) =1 + ) +select max(num) as num +from cte ``` [1045. Customers Who Bought All Products](https://leetcode.com/problems/customers-who-bought-all-products/) @@ -351,12 +447,16 @@ HAVING COUNT(DISTINCT product_key) = ( ](https://leetcode.com/problems/the-number-of-employees-which-report-to-each-employee/) ```sql -SELECT e1.employee_id, e1.name, COUNT(e2.employee_id) reports_count, ROUND(AVG(e2.age)) average_age -FROM Employees e1, Employees e2 -WHERE e1.employee_id = e2.reports_to -GROUP BY e1.employee_id -HAVING reports_count > 0 -ORDER BY e1.employee_id + +select e1.employee_id, e1.name, count(e2.employee_id) as reports_count, + round(avg(e2.age)) as average_age + +from Employees e1 +left join Employees e2 +On e1.employee_id = e2.reports_to +group by e1.employee_id, e1.name +having count(e2.employee_id) >=1 +order by 1 ``` [1789. Primary Department for Each Employee ](https://leetcode.com/problems/primary-department-for-each-employee/?envType=study-plan-v2&envId=top-sql-50) @@ -431,14 +531,13 @@ WHEN product_id NOT IN [1978. Employees Whose Manager Left the Company](https://leetcode.com/problems/employees-whose-manager-left-the-company) ```sql -SELECT employee_id -FROM Employees -WHERE manager_id NOT IN ( - SELECT employee_id - FROM Employees -) -AND salary < 30000 -ORDER BY employee_id +select employee_id + +from Employees +where salary < 30000 and manager_id not in ( + select employee_id from Employees + ) +order by employee_id ``` [185. Department Top Three Salaries](https://leetcode.com/problems/department-top-three-salaries) @@ -465,6 +564,12 @@ WHERE r.salary_rank <=3; SELECT user_id, CONCAT(UPPER(LEFT(name, 1)), LOWER(RIGHT(name, LENGTH(name)-1))) AS name FROM Users ORDER BY user_id + +--OR Using Concat and substr functions +select user_id, + concat(Upper(substr(name,1,1)), lower(substr(name,2))) as name +from Users +order by user_id ``` [1527. Patients With a Condition](https://leetcode.com/problems/patients-with-a-condition) @@ -481,6 +586,17 @@ DELETE p FROM Person p, Person q WHERE p.id > q.id AND q.Email = p.Email + +-- OR best approach scalable for large datasets +DELETE FROM person +WHERE id NOT IN ( + SELECT * FROM ( + SELECT MIN(id) + FROM person + GROUP BY email + ) AS valid_ids +); + ``` [176. Second Highest Salary](https://leetcode.com/problems/second-highest-salary) @@ -502,6 +618,12 @@ AS SecondHighestSalary SELECT * FROM Users WHERE mail REGEXP '^[A-Za-z][A-Za-z0-9_\.\-]*@leetcode\\.com$' + +-- OR slight regex variation don't forgot to use REGEXP keyword and single quotes around the expression + +select * +from Users +where mail REGEXP '^[a-zA-Z][a-zA-Z0-9._-]*@leetcode\\.com$' ``` [1204. Last Person to Fit in the Bus](https://leetcode.com/problems/last-person-to-fit-in-the-bus/) @@ -539,11 +661,22 @@ FROM Accounts -- swap every two consecutives -- num(students): odd? no swap for last one -SELECT id, -CASE WHEN MOD(id,2)=0 THEN (LAG(student) OVER (ORDER BY id)) -ELSE (LEAD(student, 1, student) OVER (ORDER BY id)) -END AS 'Student' -FROM Seat +select id, +case when + mod(id,2) = 0 then lag(student) over(order by id) + else lead(student,1,student) over(order by id) end + as student +from Seat + +-- Very easy way. +-- here id in order by is id column we created with case when. not original id column present in the table + +select case when id % 2 =1 and id+1 in (select id from Seat) then id+1 + when id % 2 =0 then id-1 + else id + end as id,student + from Seat + order by id; ``` [1327. List the Products Ordered in a Period](https://leetcode.com/problems/list-the-products-ordered-in-a-period/) @@ -558,6 +691,17 @@ ON p.product_id = o.product_id WHERE DATE_FORMAT(order_date, '%Y-%m') = '2020-02' GROUP BY p.product_name HAVING SUM(o.unit) >= 100 + +-- OR we can do like this without using date_format + +select product_name, sum(unit) as unit +from Orders o +left join Products p +on o.product_id = p.product_id +where order_date between '2020-02-01' and '2020-02-29' +group by o.product_id, product_name +having sum(unit) >= 100 + ``` [1484. Group Sold Products By The Date](https://leetcode.com/problems/group-sold-products-by-the-date/) @@ -568,6 +712,14 @@ GROUP_CONCAT(DISTINCT product) AS 'products' FROM Activities GROUP BY sell_date ORDER BY sell_date + +-- little modification + +select sell_date, count(distinct product) as num_sold, + group_concat(distinct product order by product asc) as products + +from Activities +group by sell_date ``` [1341. Movie Rating](https://leetcode.com/problems/movie-rating/) @@ -602,9 +754,9 @@ SELECT visited_on, amount, ROUND(amount/7, 2) AS average_amount FROM ( SELECT DISTINCT visited_on, SUM(amount) OVER(ORDER BY visited_on RANGE BETWEEN INTERVAL 6 DAY PRECEDING AND CURRENT ROW) AS amount, - MIN(visited_on) OVER() day_1 + MIN(visited_on) OVER() as day_1 FROM Customer -) t +) t1 WHERE visited_on >= day_1+6; ```