272 lines
8.9 KiB
MySQL
272 lines
8.9 KiB
MySQL
|
|
-- Database Schema Fixes and Optimizations
|
||
|
|
-- Generated: 2026-01-16
|
||
|
|
|
||
|
|
-- ==========================================
|
||
|
|
-- 1. ADD MISSING COLUMNS
|
||
|
|
-- ==========================================
|
||
|
|
|
||
|
|
-- Add missing timestamps to products if not exists
|
||
|
|
DO $$
|
||
|
|
BEGIN
|
||
|
|
IF NOT EXISTS (
|
||
|
|
SELECT 1 FROM information_schema.columns
|
||
|
|
WHERE table_name = 'products' AND column_name = 'deleted_at'
|
||
|
|
) THEN
|
||
|
|
ALTER TABLE products ADD COLUMN deleted_at TIMESTAMP;
|
||
|
|
END IF;
|
||
|
|
END $$;
|
||
|
|
|
||
|
|
-- Add missing order columns
|
||
|
|
DO $$
|
||
|
|
BEGIN
|
||
|
|
IF NOT EXISTS (
|
||
|
|
SELECT 1 FROM information_schema.columns
|
||
|
|
WHERE table_name = 'orders' AND column_name = 'customer_id'
|
||
|
|
) THEN
|
||
|
|
ALTER TABLE orders ADD COLUMN customer_id UUID;
|
||
|
|
ALTER TABLE orders ADD CONSTRAINT fk_orders_customer
|
||
|
|
FOREIGN KEY (customer_id) REFERENCES customers(id) ON DELETE SET NULL;
|
||
|
|
END IF;
|
||
|
|
END $$;
|
||
|
|
|
||
|
|
DO $$
|
||
|
|
BEGIN
|
||
|
|
IF NOT EXISTS (
|
||
|
|
SELECT 1 FROM information_schema.columns
|
||
|
|
WHERE table_name = 'orders' AND column_name = 'shipping_address'
|
||
|
|
) THEN
|
||
|
|
ALTER TABLE orders ADD COLUMN shipping_address JSONB;
|
||
|
|
ALTER TABLE orders ADD COLUMN billing_address JSONB;
|
||
|
|
ALTER TABLE orders ADD COLUMN payment_method VARCHAR(50);
|
||
|
|
ALTER TABLE orders ADD COLUMN tracking_number VARCHAR(100);
|
||
|
|
ALTER TABLE orders ADD COLUMN notes TEXT;
|
||
|
|
ALTER TABLE orders ADD COLUMN created_at TIMESTAMP DEFAULT NOW();
|
||
|
|
END IF;
|
||
|
|
END $$;
|
||
|
|
|
||
|
|
-- ==========================================
|
||
|
|
-- 2. FIX DATA TYPE INCONSISTENCIES
|
||
|
|
-- ==========================================
|
||
|
|
|
||
|
|
-- Ensure consistent boolean defaults
|
||
|
|
ALTER TABLE products ALTER COLUMN isfeatured SET DEFAULT false;
|
||
|
|
ALTER TABLE products ALTER COLUMN isbestseller SET DEFAULT false;
|
||
|
|
ALTER TABLE products ALTER COLUMN isactive SET DEFAULT true;
|
||
|
|
|
||
|
|
ALTER TABLE product_images ALTER COLUMN is_primary SET DEFAULT false;
|
||
|
|
ALTER TABLE product_images ALTER COLUMN display_order SET DEFAULT 0;
|
||
|
|
ALTER TABLE product_images ALTER COLUMN variant_stock SET DEFAULT 0;
|
||
|
|
|
||
|
|
ALTER TABLE blogposts ALTER COLUMN ispublished SET DEFAULT false;
|
||
|
|
ALTER TABLE blogposts ALTER COLUMN isactive SET DEFAULT true;
|
||
|
|
|
||
|
|
ALTER TABLE pages ALTER COLUMN ispublished SET DEFAULT true;
|
||
|
|
ALTER TABLE pages ALTER COLUMN isactive SET DEFAULT true;
|
||
|
|
|
||
|
|
ALTER TABLE portfolioprojects ALTER COLUMN isactive SET DEFAULT true;
|
||
|
|
|
||
|
|
-- ==========================================
|
||
|
|
-- 3. ADD MISSING INDEXES FOR PERFORMANCE
|
||
|
|
-- ==========================================
|
||
|
|
|
||
|
|
-- Products: Add indexes for common queries
|
||
|
|
CREATE INDEX IF NOT EXISTS idx_products_active_bestseller
|
||
|
|
ON products(isactive, isbestseller) WHERE isactive = true AND isbestseller = true;
|
||
|
|
|
||
|
|
CREATE INDEX IF NOT EXISTS idx_products_category_active
|
||
|
|
ON products(category, isactive) WHERE isactive = true;
|
||
|
|
|
||
|
|
CREATE INDEX IF NOT EXISTS idx_products_price_range
|
||
|
|
ON products(price) WHERE isactive = true;
|
||
|
|
|
||
|
|
CREATE INDEX IF NOT EXISTS idx_products_stock
|
||
|
|
ON products(stockquantity) WHERE isactive = true;
|
||
|
|
|
||
|
|
-- Product Images: Optimize joins
|
||
|
|
CREATE INDEX IF NOT EXISTS idx_product_images_product_order
|
||
|
|
ON product_images(product_id, display_order);
|
||
|
|
|
||
|
|
-- Blog: Add missing indexes
|
||
|
|
CREATE INDEX IF NOT EXISTS idx_blogposts_published_date
|
||
|
|
ON blogposts(publisheddate DESC) WHERE ispublished = true;
|
||
|
|
|
||
|
|
CREATE INDEX IF NOT EXISTS idx_blogposts_category
|
||
|
|
ON blogposts(category) WHERE ispublished = true;
|
||
|
|
|
||
|
|
-- Pages: Add index for active lookup
|
||
|
|
CREATE INDEX IF NOT EXISTS idx_pages_slug_active
|
||
|
|
ON pages(slug) WHERE isactive = true AND ispublished = true;
|
||
|
|
|
||
|
|
-- Orders: Add indexes for queries
|
||
|
|
CREATE INDEX IF NOT EXISTS idx_orders_customer
|
||
|
|
ON orders(customer_id) WHERE customer_id IS NOT NULL;
|
||
|
|
|
||
|
|
CREATE INDEX IF NOT EXISTS idx_orders_status
|
||
|
|
ON orders(orderstatus);
|
||
|
|
|
||
|
|
CREATE INDEX IF NOT EXISTS idx_orders_date
|
||
|
|
ON orders(orderdate DESC);
|
||
|
|
|
||
|
|
CREATE INDEX IF NOT EXISTS idx_orders_number
|
||
|
|
ON orders(ordernumber);
|
||
|
|
|
||
|
|
-- Customers: Add indexes
|
||
|
|
CREATE INDEX IF NOT EXISTS idx_customers_email_active
|
||
|
|
ON customers(email) WHERE is_active = true;
|
||
|
|
|
||
|
|
CREATE INDEX IF NOT EXISTS idx_customers_created
|
||
|
|
ON customers(created_at DESC);
|
||
|
|
|
||
|
|
-- ==========================================
|
||
|
|
-- 4. ADD MISSING CONSTRAINTS
|
||
|
|
-- ==========================================
|
||
|
|
|
||
|
|
-- Ensure products have valid prices
|
||
|
|
DO $$
|
||
|
|
BEGIN
|
||
|
|
IF NOT EXISTS (
|
||
|
|
SELECT 1 FROM information_schema.table_constraints
|
||
|
|
WHERE constraint_name = 'chk_products_price_positive'
|
||
|
|
) THEN
|
||
|
|
ALTER TABLE products ADD CONSTRAINT chk_products_price_positive
|
||
|
|
CHECK (price >= 0);
|
||
|
|
END IF;
|
||
|
|
END $$;
|
||
|
|
|
||
|
|
DO $$
|
||
|
|
BEGIN
|
||
|
|
IF NOT EXISTS (
|
||
|
|
SELECT 1 FROM information_schema.table_constraints
|
||
|
|
WHERE constraint_name = 'chk_products_stock_nonnegative'
|
||
|
|
) THEN
|
||
|
|
ALTER TABLE products ADD CONSTRAINT chk_products_stock_nonnegative
|
||
|
|
CHECK (stockquantity >= 0);
|
||
|
|
END IF;
|
||
|
|
END $$;
|
||
|
|
|
||
|
|
-- Ensure product images have valid ordering
|
||
|
|
DO $$
|
||
|
|
BEGIN
|
||
|
|
IF NOT EXISTS (
|
||
|
|
SELECT 1 FROM information_schema.table_constraints
|
||
|
|
WHERE constraint_name = 'chk_product_images_order_nonnegative'
|
||
|
|
) THEN
|
||
|
|
ALTER TABLE product_images ADD CONSTRAINT chk_product_images_order_nonnegative
|
||
|
|
CHECK (display_order >= 0);
|
||
|
|
END IF;
|
||
|
|
END $$;
|
||
|
|
|
||
|
|
DO $$
|
||
|
|
BEGIN
|
||
|
|
IF NOT EXISTS (
|
||
|
|
SELECT 1 FROM information_schema.table_constraints
|
||
|
|
WHERE constraint_name = 'chk_product_images_stock_nonnegative'
|
||
|
|
) THEN
|
||
|
|
ALTER TABLE product_images ADD CONSTRAINT chk_product_images_stock_nonnegative
|
||
|
|
CHECK (variant_stock >= 0);
|
||
|
|
END IF;
|
||
|
|
END $$;
|
||
|
|
|
||
|
|
-- Ensure orders have valid amounts
|
||
|
|
ALTER TABLE orders DROP CONSTRAINT IF EXISTS chk_orders_amounts;
|
||
|
|
ALTER TABLE orders ADD CONSTRAINT chk_orders_amounts
|
||
|
|
CHECK (subtotal >= 0 AND total >= 0);
|
||
|
|
|
||
|
|
-- ==========================================
|
||
|
|
-- 5. ADD CASCADE DELETE FOR ORPHANED DATA
|
||
|
|
-- ==========================================
|
||
|
|
|
||
|
|
-- Ensure product images are deleted when product is deleted
|
||
|
|
ALTER TABLE product_images DROP CONSTRAINT IF EXISTS product_images_product_id_fkey;
|
||
|
|
ALTER TABLE product_images ADD CONSTRAINT product_images_product_id_fkey
|
||
|
|
FOREIGN KEY (product_id) REFERENCES products(id) ON DELETE CASCADE;
|
||
|
|
|
||
|
|
-- ==========================================
|
||
|
|
-- 6. CREATE MISSING TABLES
|
||
|
|
-- ==========================================
|
||
|
|
|
||
|
|
-- Create order_items if missing
|
||
|
|
CREATE TABLE IF NOT EXISTS order_items (
|
||
|
|
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::TEXT,
|
||
|
|
order_id TEXT NOT NULL REFERENCES orders(id) ON DELETE CASCADE,
|
||
|
|
product_id TEXT REFERENCES products(id) ON DELETE SET NULL,
|
||
|
|
product_name VARCHAR(255) NOT NULL,
|
||
|
|
product_sku VARCHAR(100),
|
||
|
|
quantity INTEGER NOT NULL CHECK (quantity > 0),
|
||
|
|
unit_price NUMERIC(10,2) NOT NULL CHECK (unit_price >= 0),
|
||
|
|
total_price NUMERIC(10,2) NOT NULL CHECK (total_price >= 0),
|
||
|
|
color_variant VARCHAR(100),
|
||
|
|
created_at TIMESTAMP DEFAULT NOW()
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE INDEX IF NOT EXISTS idx_order_items_order ON order_items(order_id);
|
||
|
|
CREATE INDEX IF NOT EXISTS idx_order_items_product ON order_items(product_id);
|
||
|
|
|
||
|
|
-- Create reviews table if missing
|
||
|
|
CREATE TABLE IF NOT EXISTS product_reviews (
|
||
|
|
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::TEXT,
|
||
|
|
product_id TEXT NOT NULL REFERENCES products(id) ON DELETE CASCADE,
|
||
|
|
customer_id UUID REFERENCES customers(id) ON DELETE CASCADE,
|
||
|
|
rating INTEGER NOT NULL CHECK (rating >= 1 AND rating <= 5),
|
||
|
|
title VARCHAR(200),
|
||
|
|
comment TEXT,
|
||
|
|
is_verified_purchase BOOLEAN DEFAULT false,
|
||
|
|
is_approved BOOLEAN DEFAULT false,
|
||
|
|
helpful_count INTEGER DEFAULT 0,
|
||
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
||
|
|
updated_at TIMESTAMP DEFAULT NOW()
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE INDEX IF NOT EXISTS idx_reviews_product ON product_reviews(product_id);
|
||
|
|
CREATE INDEX IF NOT EXISTS idx_reviews_customer ON product_reviews(customer_id);
|
||
|
|
CREATE INDEX IF NOT EXISTS idx_reviews_approved ON product_reviews(is_approved) WHERE is_approved = true;
|
||
|
|
|
||
|
|
-- ==========================================
|
||
|
|
-- 7. ADD TRIGGERS FOR AUTOMATIC TIMESTAMPS
|
||
|
|
-- ==========================================
|
||
|
|
|
||
|
|
-- Function to update updated_at timestamp
|
||
|
|
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||
|
|
RETURNS TRIGGER AS $$
|
||
|
|
BEGIN
|
||
|
|
NEW.updatedat = NOW();
|
||
|
|
RETURN NEW;
|
||
|
|
END;
|
||
|
|
$$ language 'plpgsql';
|
||
|
|
|
||
|
|
-- Add triggers for products
|
||
|
|
DROP TRIGGER IF EXISTS update_products_updatedat ON products;
|
||
|
|
CREATE TRIGGER update_products_updatedat
|
||
|
|
BEFORE UPDATE ON products
|
||
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||
|
|
|
||
|
|
-- Add triggers for blogposts
|
||
|
|
DROP TRIGGER IF EXISTS update_blogposts_updatedat ON blogposts;
|
||
|
|
CREATE TRIGGER update_blogposts_updatedat
|
||
|
|
BEFORE UPDATE ON blogposts
|
||
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||
|
|
|
||
|
|
-- Add triggers for pages
|
||
|
|
DROP TRIGGER IF EXISTS update_pages_updatedat ON pages;
|
||
|
|
CREATE TRIGGER update_pages_updatedat
|
||
|
|
BEFORE UPDATE ON pages
|
||
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||
|
|
|
||
|
|
-- ==========================================
|
||
|
|
-- 8. UPDATE STATISTICS FOR QUERY PLANNER
|
||
|
|
-- ==========================================
|
||
|
|
|
||
|
|
ANALYZE products;
|
||
|
|
ANALYZE product_images;
|
||
|
|
ANALYZE blogposts;
|
||
|
|
ANALYZE pages;
|
||
|
|
ANALYZE portfolioprojects;
|
||
|
|
ANALYZE orders;
|
||
|
|
ANALYZE customers;
|
||
|
|
|
||
|
|
-- ==========================================
|
||
|
|
-- COMPLETE
|
||
|
|
-- ==========================================
|
||
|
|
|
||
|
|
SELECT 'Database schema fixes applied successfully!' AS status;
|