Skip to content

Cleaning a PostgreSQL Database

In this project, data used is from a hypothetical Super Store to showcase SQL skills in data cleaning. This project will identify top categories based on the highest profit margins and detecting missing values, utilizing comprehensive knowledge of SQL concepts.

Data Dictionary:

orders:

ColumnDefinitionData typeComments
row_idUnique Record IDINTEGER
order_idIdentifier for each order in tableTEXTConnects to order_id in returned_orders table
order_dateDate when order was placedTEXT
marketMarket order_id belongs toTEXT
regionRegion Customer belongs toTEXTConnects to region in people table
product_idIdentifier of Product boughtTEXTConnects to product_id in products table
salesTotal Sales Amount for the Line ItemDOUBLE PRECISION
quantityTotal Quantity for the Line ItemDOUBLE PRECISION
discountDiscount applied for the Line ItemDOUBLE PRECISION
profitTotal Profit earned on the Line ItemDOUBLE PRECISION

returned_orders:

ColumnDefinitionData type
returnedYes values for Order / Line Item ReturnedTEXT
order_idIdentifier for each order in tableTEXT
marketMarket order_id belongs toTEXT

people:

ColumnDefinitionData type
personName of Salesperson credited with OrderTEXT
regionRegion Salesperson in operating inTEXT

products:

ColumnDefinitionData type
product_idUnique Identifier for the ProductTEXT
categoryCategory Product belongs toTEXT
sub_categorySub Category Product belongs toTEXT
product_nameDetailed Name of the ProductTEXT

As you can see in the Data Dictionary above, date fields have been written to the orders table as TEXT and numeric fields like sales, profit, etc. have been written to the orders table as Double Precision. This will be corrected in some of the queries.

Spinner
DataFrameas
top_five_products_each_category
variable
-- top_five_products_each_category

WITH top AS (
SELECT category, product_name, 
	SUM(sales) AS product_total_sales, 
	SUM(profit) AS product_total_profit, 
	RANK() OVER(PARTITION BY category ORDER BY SUM(sales) DESC) AS product_rank
FROM orders 
INNER JOIN products 
USING(product_id)
GROUP BY category, product_name)

SELECT category, product_name, product_total_sales, product_total_profit, product_rank
FROM top 
WHERE product_rank <=5
ORDER BY category, product_total_sales DESC;
Spinner
DataFrameas
impute_missing_values
variable
-- impute_missing_values

WITH missing AS(
SELECT product_id, 
	discount::NUMERIC,
	market,
	region,
	sales::NUMERIC,
	quantity::NUMERIC
FROM orders 
WHERE quantity IS NULL),
base AS(
SELECT o.product_id, 
	o.sales::NUMERIC/o.quantity::NUMERIC AS base_price
FROM orders AS o
RIGHT JOIN missing AS m
ON o.product_id = m.product_id
	AND o.discount = m.discount
WHERE o.quantity IS NOT NULL)

SELECT DISTINCT m.*,
	ROUND(m.sales/base_price,0) AS calculated_quantity
FROM missing AS m
INNER JOIN base AS b
USING(product_id);