Skip to content

DuckDB Parquet Data Quality Inspection: Finding Bad Data Files

Objective: Master DuckDB for comprehensive Parquet file inspection and data quality analysis. When you need to identify corrupted files, when you want to detect data anomalies, when you're building data quality pipelines—DuckDB becomes your weapon of choice.

DuckDB provides the foundation for efficient Parquet file inspection and data quality analysis. Without proper understanding of file validation, data profiling, and anomaly detection, you're building unreliable data pipelines that miss the power of automated data quality assessment. This guide shows you how to wield DuckDB with the precision of a senior data engineer.

0) Prerequisites (Read Once, Live by Them)

The Five Commandments

  1. Understand DuckDB fundamentals
  2. Parquet file reading and writing
  3. SQL query capabilities
  4. Performance optimization
  5. File system integration

  6. Master data quality patterns

  7. File corruption detection
  8. Schema validation
  9. Data profiling and statistics
  10. Anomaly detection algorithms

  11. Know your inspection techniques

  12. Metadata analysis
  13. Row group inspection
  14. Column statistics
  15. Data type validation

  16. Validate everything

  17. File integrity checks
  18. Schema consistency
  19. Data completeness
  20. Performance under scale

  21. Plan for production

  22. Scalable inspection pipelines
  23. Monitoring and alerting
  24. Automated remediation
  25. Integration with existing systems

Why These Principles: DuckDB mastery is the foundation of reliable data quality inspection. Understanding file validation, mastering data profiling, and following best practices is essential for building production-ready data quality systems.

1) What is DuckDB Parquet Inspection? (The Foundation)

Core Concepts

# DuckDB enables efficient Parquet file inspection
duckdb_inspection:
  file_validation:
    - "File corruption detection"
    - "Schema validation"
    - "Metadata inspection"
    - "Row group analysis"

  data_quality:
    - "Statistical profiling"
    - "Anomaly detection"
    - "Completeness analysis"
    - "Consistency validation"

  performance:
    - "Fast file scanning"
    - "Parallel processing"
    - "Memory optimization"
    - "Query optimization"

DuckDB Architecture

graph TB
    subgraph "Data Sources"
        A[Parquet Files]
        B[Directory Trees]
        C[Cloud Storage]
        D[File Systems]
    end

    subgraph "DuckDB Engine"
        E[File Scanner]
        F[Metadata Reader]
        G[Schema Validator]
        H[Statistics Engine]
    end

    subgraph "Inspection Layer"
        I[File Integrity]
        J[Data Profiling]
        K[Anomaly Detection]
        L[Quality Metrics]
    end

    subgraph "Output"
        M[Quality Reports]
        N[Bad File Lists]
        O[Statistics]
        P[Recommendations]
    end

    A --> E
    B --> E
    C --> E
    D --> E
    E --> F
    F --> G
    G --> H
    H --> I
    I --> J
    J --> K
    K --> L
    L --> M
    L --> N
    L --> O
    L --> P

    style A fill:#e1f5fe
    style E fill:#f3e5f5
    style I fill:#e8f5e8
    style M fill:#fff3e0
    style N fill:#fce4ec
    style O fill:#f1f8e9
    style P fill:#e0f2f1

Why DuckDB for Parquet Inspection

# DuckDB enables efficient Parquet file inspection
import duckdb

# Connect to DuckDB
conn = duckdb.connect()

# Scan directory for Parquet files
files = conn.execute("""
    SELECT file_name, file_size, file_modified
    FROM glob('data/*.parquet')
    ORDER BY file_modified DESC
""").fetchall()

# Inspect file metadata
for file_name, file_size, file_modified in files:
    print(f"File: {file_name}")
    print(f"Size: {file_size} bytes")
    print(f"Modified: {file_modified}")

Why DuckDB Matters: DuckDB provides fast, SQL-based access to Parquet files with built-in optimization for analytical workloads. It enables comprehensive data quality inspection without the overhead of traditional database systems.

2) File Integrity Inspection (The Foundation)

Basic File Validation

# Basic file integrity inspection
import duckdb
import os
from pathlib import Path

def inspect_parquet_files(directory: str):
    """Inspect Parquet files for basic integrity issues"""
    conn = duckdb.connect()

    # Get all Parquet files in directory
    query = f"""
    SELECT 
        file_name,
        file_size,
        file_modified,
        CASE 
            WHEN file_size = 0 THEN 'EMPTY_FILE'
            WHEN file_size < 1000 THEN 'SUSPICIOUSLY_SMALL'
            ELSE 'OK'
        END as file_status
    FROM glob('{directory}/**/*.parquet')
    ORDER BY file_size ASC
    """

    results = conn.execute(query).fetchall()

    print("File Integrity Inspection Results:")
    print("=" * 50)

    for file_name, file_size, file_modified, status in results:
        print(f"File: {file_name}")
        print(f"Size: {file_size} bytes")
        print(f"Status: {status}")
        print(f"Modified: {file_modified}")
        print("-" * 30)

    return results

def detect_corrupted_files(directory: str):
    """Detect potentially corrupted Parquet files"""
    conn = duckdb.connect()

    corrupted_files = []

    # Get all Parquet files
    files = conn.execute(f"SELECT file_name FROM glob('{directory}/**/*.parquet')").fetchall()

    for (file_name,) in files:
        try:
            # Try to read file metadata
            metadata_query = f"""
            SELECT COUNT(*) as row_count
            FROM read_parquet('{file_name}')
            LIMIT 1
            """
            conn.execute(metadata_query).fetchone()

        except Exception as e:
            print(f"Corrupted file detected: {file_name}")
            print(f"Error: {e}")
            corrupted_files.append(file_name)

    return corrupted_files

Advanced File Inspection

# Advanced file inspection with detailed metadata
def advanced_file_inspection(directory: str):
    """Advanced Parquet file inspection with detailed metadata"""
    conn = duckdb.connect()

    # Create comprehensive file inspection query
    query = f"""
    WITH file_info AS (
        SELECT 
            file_name,
            file_size,
            file_modified,
            -- Try to get row count
            CASE 
                WHEN file_size > 0 THEN
                    (SELECT COUNT(*) FROM read_parquet(file_name) LIMIT 1000000)
                ELSE 0
            END as estimated_rows,
            -- Try to get column count
            CASE 
                WHEN file_size > 0 THEN
                    (SELECT COUNT(*) FROM pragma_table_info('read_parquet(file_name)'))
                ELSE 0
            END as column_count
        FROM glob('{directory}/**/*.parquet')
    )
    SELECT 
        file_name,
        file_size,
        file_modified,
        estimated_rows,
        column_count,
        CASE 
            WHEN file_size = 0 THEN 'EMPTY_FILE'
            WHEN file_size < 1000 THEN 'SUSPICIOUSLY_SMALL'
            WHEN estimated_rows = 0 AND file_size > 1000 THEN 'POTENTIALLY_CORRUPTED'
            WHEN column_count = 0 THEN 'SCHEMA_ISSUE'
            ELSE 'OK'
        END as file_status,
        ROUND(file_size / 1024.0 / 1024.0, 2) as size_mb
    FROM file_info
    ORDER BY file_status, file_size ASC
    """

    results = conn.execute(query).fetchall()

    # Group results by status
    status_groups = {}
    for result in results:
        status = result[5]  # file_status
        if status not in status_groups:
            status_groups[status] = []
        status_groups[status].append(result)

    # Print results by status
    for status, files in status_groups.items():
        print(f"\n{status} Files ({len(files)} files):")
        print("=" * 50)
        for file_name, file_size, file_modified, estimated_rows, column_count, _, size_mb in files:
            print(f"File: {file_name}")
            print(f"Size: {size_mb} MB ({file_size} bytes)")
            print(f"Rows: {estimated_rows}")
            print(f"Columns: {column_count}")
            print(f"Modified: {file_modified}")
            print("-" * 30)

    return results

3) Schema Validation (The Structure)

Schema Consistency Analysis

# Schema validation and consistency analysis
def validate_schemas(directory: str):
    """Validate schema consistency across Parquet files"""
    conn = duckdb.connect()

    # Get schema information for all files
    query = f"""
    WITH schema_info AS (
        SELECT 
            file_name,
            column_name,
            data_type,
            is_nullable
        FROM (
            SELECT 
                file_name,
                unnest(column_names) as column_name,
                unnest(column_types) as data_type,
                unnest(column_nullable) as is_nullable
            FROM (
                SELECT 
                    file_name,
                    list(column_name) as column_names,
                    list(data_type) as column_types,
                    list(nullable) as column_nullable
                FROM (
                    SELECT 
                        file_name,
                        column_name,
                        data_type,
                        nullable
                    FROM glob('{directory}/**/*.parquet') as files,
                    pragma_table_info('read_parquet(files.file_name)')
                )
                GROUP BY file_name
            )
        )
    )
    SELECT 
        file_name,
        column_name,
        data_type,
        is_nullable
    FROM schema_info
    ORDER BY file_name, column_name
    """

    results = conn.execute(query).fetchall()

    # Analyze schema consistency
    schema_analysis = {}
    for file_name, column_name, data_type, is_nullable in results:
        if column_name not in schema_analysis:
            schema_analysis[column_name] = {
                'data_types': set(),
                'nullable_values': set(),
                'files': set()
            }

        schema_analysis[column_name]['data_types'].add(data_type)
        schema_analysis[column_name]['nullable_values'].add(is_nullable)
        schema_analysis[column_name]['files'].add(file_name)

    # Report schema inconsistencies
    print("Schema Consistency Analysis:")
    print("=" * 50)

    for column_name, info in schema_analysis.items():
        if len(info['data_types']) > 1:
            print(f"INCONSISTENT: Column '{column_name}' has multiple types: {info['data_types']}")
            print(f"Files: {info['files']}")
            print("-" * 30)
        elif len(info['nullable_values']) > 1:
            print(f"INCONSISTENT: Column '{column_name}' has mixed nullable values: {info['nullable_values']}")
            print(f"Files: {info['files']}")
            print("-" * 30)

    return schema_analysis

def detect_schema_drift(directory: str, reference_schema: dict):
    """Detect schema drift from reference schema"""
    conn = duckdb.connect()

    # Get current schema
    current_schema = validate_schemas(directory)

    # Compare with reference schema
    drift_detected = []

    for column_name, info in current_schema.items():
        if column_name in reference_schema:
            ref_info = reference_schema[column_name]

            # Check for type drift
            if info['data_types'] != ref_info['data_types']:
                drift_detected.append({
                    'column': column_name,
                    'type': 'TYPE_DRIFT',
                    'current': info['data_types'],
                    'reference': ref_info['data_types']
                })

            # Check for nullable drift
            if info['nullable_values'] != ref_info['nullable_values']:
                drift_detected.append({
                    'column': column_name,
                    'type': 'NULLABLE_DRIFT',
                    'current': info['nullable_values'],
                    'reference': ref_info['nullable_values']
                })
        else:
            # New column detected
            drift_detected.append({
                'column': column_name,
                'type': 'NEW_COLUMN',
                'current': info['data_types'],
                'reference': 'N/A'
            })

    # Check for removed columns
    for column_name in reference_schema:
        if column_name not in current_schema:
            drift_detected.append({
                'column': column_name,
                'type': 'REMOVED_COLUMN',
                'current': 'N/A',
                'reference': reference_schema[column_name]['data_types']
            })

    return drift_detected

4) Data Quality Profiling (The Analysis)

Statistical Data Profiling

# Statistical data profiling for quality assessment
def profile_data_quality(directory: str):
    """Profile data quality across Parquet files"""
    conn = duckdb.connect()

    # Get all files
    files = conn.execute(f"SELECT file_name FROM glob('{directory}/**/*.parquet')").fetchall()

    quality_report = {}

    for (file_name,) in files:
        try:
            # Get basic statistics
            stats_query = f"""
            SELECT 
                COUNT(*) as total_rows,
                COUNT(DISTINCT *) as unique_rows,
                COUNT(*) - COUNT(DISTINCT *) as duplicate_rows
            FROM read_parquet('{file_name}')
            """

            stats = conn.execute(stats_query).fetchone()
            total_rows, unique_rows, duplicate_rows = stats

            # Get column statistics
            column_stats_query = f"""
            SELECT 
                column_name,
                data_type,
                COUNT(*) as non_null_count,
                COUNT(*) - COUNT(column_name) as null_count,
                COUNT(DISTINCT column_name) as distinct_count
            FROM (
                SELECT 
                    unnest(column_names) as column_name,
                    unnest(column_types) as data_type
                FROM (
                    SELECT 
                        list(column_name) as column_names,
                        list(data_type) as column_types
                    FROM pragma_table_info('read_parquet('{file_name}'))
                )
            )
            GROUP BY column_name, data_type
            """

            column_stats = conn.execute(column_stats_query).fetchall()

            # Calculate quality metrics
            quality_metrics = {
                'total_rows': total_rows,
                'unique_rows': unique_rows,
                'duplicate_rows': duplicate_rows,
                'duplicate_percentage': (duplicate_rows / total_rows * 100) if total_rows > 0 else 0,
                'columns': {}
            }

            for column_name, data_type, non_null_count, null_count, distinct_count in column_stats:
                quality_metrics['columns'][column_name] = {
                    'data_type': data_type,
                    'non_null_count': non_null_count,
                    'null_count': null_count,
                    'distinct_count': distinct_count,
                    'null_percentage': (null_count / total_rows * 100) if total_rows > 0 else 0,
                    'distinct_percentage': (distinct_count / total_rows * 100) if total_rows > 0 else 0
                }

            quality_report[file_name] = quality_metrics

        except Exception as e:
            print(f"Error profiling file {file_name}: {e}")
            quality_report[file_name] = {'error': str(e)}

    return quality_report

def detect_data_anomalies(directory: str):
    """Detect data anomalies in Parquet files"""
    conn = duckdb.connect()

    anomalies = []

    # Get all files
    files = conn.execute(f"SELECT file_name FROM glob('{directory}/**/*.parquet')").fetchall()

    for (file_name,) in files:
        try:
            # Check for empty files
            row_count_query = f"SELECT COUNT(*) FROM read_parquet('{file_name}')"
            row_count = conn.execute(row_count_query).fetchone()[0]

            if row_count == 0:
                anomalies.append({
                    'file': file_name,
                    'type': 'EMPTY_FILE',
                    'description': 'File contains no data rows'
                })
                continue

            # Check for suspicious patterns
            # Check for all null columns
            null_columns_query = f"""
            SELECT 
                column_name,
                COUNT(*) as total_count,
                COUNT(column_name) as non_null_count,
                COUNT(*) - COUNT(column_name) as null_count
            FROM (
                SELECT 
                    unnest(column_names) as column_name
                FROM (
                    SELECT 
                        list(column_name) as column_names
                    FROM pragma_table_info('read_parquet('{file_name}'))
                )
            )
            GROUP BY column_name
            HAVING COUNT(*) - COUNT(column_name) = COUNT(*)
            """

            null_columns = conn.execute(null_columns_query).fetchall()
            for column_name, total_count, non_null_count, null_count in null_columns:
                anomalies.append({
                    'file': file_name,
                    'type': 'ALL_NULL_COLUMN',
                    'description': f'Column {column_name} is completely null'
                })

            # Check for duplicate rows
            duplicate_query = f"""
            SELECT 
                COUNT(*) as total_rows,
                COUNT(DISTINCT *) as unique_rows
            FROM read_parquet('{file_name}')
            """

            total_rows, unique_rows = conn.execute(duplicate_query).fetchone()
            if total_rows > unique_rows:
                duplicate_percentage = ((total_rows - unique_rows) / total_rows) * 100
                anomalies.append({
                    'file': file_name,
                    'type': 'HIGH_DUPLICATE_RATE',
                    'description': f'{duplicate_percentage:.2f}% of rows are duplicates'
                })

        except Exception as e:
            anomalies.append({
                'file': file_name,
                'type': 'PROCESSING_ERROR',
                'description': f'Error processing file: {e}'
            })

    return anomalies

5) Performance and Optimization (The Speed)

Efficient File Scanning

# Performance optimization for large-scale file inspection
def optimized_file_scanning(directory: str, batch_size: int = 100):
    """Optimized file scanning for large directories"""
    conn = duckdb.connect()

    # Configure DuckDB for performance
    conn.execute("SET memory_limit='4GB'")
    conn.execute("SET threads=4")

    # Get all files in batches
    all_files = conn.execute(f"SELECT file_name FROM glob('{directory}/**/*.parquet')").fetchall()

    results = []

    for i in range(0, len(all_files), batch_size):
        batch_files = all_files[i:i + batch_size]

        # Process batch
        batch_results = process_file_batch(conn, batch_files)
        results.extend(batch_results)

        print(f"Processed batch {i//batch_size + 1}/{(len(all_files) + batch_size - 1)//batch_size}")

    return results

def process_file_batch(conn, files):
    """Process a batch of files efficiently"""
    results = []

    for (file_name,) in files:
        try:
            # Quick file validation
            validation_query = f"""
            SELECT 
                file_name,
                file_size,
                COUNT(*) as row_count,
                COUNT(DISTINCT *) as unique_rows
            FROM read_parquet('{file_name}')
            """

            result = conn.execute(validation_query).fetchone()
            results.append({
                'file_name': file_name,
                'file_size': result[1],
                'row_count': result[2],
                'unique_rows': result[3],
                'status': 'OK'
            })

        except Exception as e:
            results.append({
                'file_name': file_name,
                'file_size': 0,
                'row_count': 0,
                'unique_rows': 0,
                'status': 'ERROR',
                'error': str(e)
            })

    return results

def parallel_file_inspection(directory: str, max_workers: int = 4):
    """Parallel file inspection for better performance"""
    import concurrent.futures
    from pathlib import Path

    # Get all files
    files = list(Path(directory).rglob("*.parquet"))

    def inspect_single_file(file_path):
        """Inspect a single file"""
        conn = duckdb.connect()

        try:
            # Basic file info
            file_size = file_path.stat().st_size

            # Try to read file
            row_count = conn.execute(f"SELECT COUNT(*) FROM read_parquet('{file_path}')").fetchone()[0]

            return {
                'file_name': str(file_path),
                'file_size': file_size,
                'row_count': row_count,
                'status': 'OK'
            }

        except Exception as e:
            return {
                'file_name': str(file_path),
                'file_size': 0,
                'row_count': 0,
                'status': 'ERROR',
                'error': str(e)
            }
        finally:
            conn.close()

    # Process files in parallel
    with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
        results = list(executor.map(inspect_single_file, files))

    return results

6) Production Data Quality Pipeline (The Scale)

Complete Data Quality Pipeline

# Complete data quality pipeline for production
class ParquetDataQualityPipeline:
    """Production-ready data quality pipeline for Parquet files"""

    def __init__(self, directory: str, config: dict = None):
        self.directory = directory
        self.config = config or self._default_config()
        self.conn = duckdb.connect()
        self._setup_connection()

    def _default_config(self):
        """Default configuration for data quality pipeline"""
        return {
            'max_file_size_mb': 1000,
            'min_file_size_bytes': 1000,
            'max_null_percentage': 50,
            'max_duplicate_percentage': 10,
            'required_columns': [],
            'excluded_files': [],
            'parallel_workers': 4
        }

    def _setup_connection(self):
        """Setup DuckDB connection with optimizations"""
        self.conn.execute("SET memory_limit='4GB'")
        self.conn.execute("SET threads=4")
        self.conn.execute("SET enable_progress_bar=true")

    def run_full_inspection(self):
        """Run complete data quality inspection"""
        print("Starting Parquet Data Quality Inspection...")
        print("=" * 50)

        # 1. File integrity check
        print("1. Checking file integrity...")
        integrity_results = self._check_file_integrity()

        # 2. Schema validation
        print("2. Validating schemas...")
        schema_results = self._validate_schemas()

        # 3. Data quality profiling
        print("3. Profiling data quality...")
        quality_results = self._profile_data_quality()

        # 4. Anomaly detection
        print("4. Detecting anomalies...")
        anomaly_results = self._detect_anomalies()

        # 5. Generate report
        print("5. Generating quality report...")
        report = self._generate_quality_report(
            integrity_results,
            schema_results,
            quality_results,
            anomaly_results
        )

        return report

    def _check_file_integrity(self):
        """Check file integrity and basic properties"""
        query = f"""
        SELECT 
            file_name,
            file_size,
            file_modified,
            CASE 
                WHEN file_size = 0 THEN 'EMPTY_FILE'
                WHEN file_size < {self.config['min_file_size_bytes']} THEN 'SUSPICIOUSLY_SMALL'
                WHEN file_size > {self.config['max_file_size_mb'] * 1024 * 1024} THEN 'SUSPICIOUSLY_LARGE'
                ELSE 'OK'
            END as integrity_status
        FROM glob('{self.directory}/**/*.parquet')
        WHERE file_name NOT IN ({','.join([f"'{f}'" for f in self.config['excluded_files']])})
        ORDER BY file_size ASC
        """

        return self.conn.execute(query).fetchall()

    def _validate_schemas(self):
        """Validate schema consistency"""
        # Implementation for schema validation
        pass

    def _profile_data_quality(self):
        """Profile data quality metrics"""
        # Implementation for data quality profiling
        pass

    def _detect_anomalies(self):
        """Detect data anomalies"""
        # Implementation for anomaly detection
        pass

    def _generate_quality_report(self, integrity, schema, quality, anomalies):
        """Generate comprehensive quality report"""
        report = {
            'summary': {
                'total_files': len(integrity),
                'corrupted_files': len([f for f in integrity if f[3] != 'OK']),
                'schema_issues': len(schema),
                'quality_issues': len(quality),
                'anomalies': len(anomalies)
            },
            'file_integrity': integrity,
            'schema_validation': schema,
            'data_quality': quality,
            'anomalies': anomalies,
            'recommendations': self._generate_recommendations(integrity, schema, quality, anomalies)
        }

        return report

    def _generate_recommendations(self, integrity, schema, quality, anomalies):
        """Generate recommendations based on findings"""
        recommendations = []

        # File integrity recommendations
        corrupted_files = [f for f in integrity if f[3] != 'OK']
        if corrupted_files:
            recommendations.append({
                'type': 'FILE_INTEGRITY',
                'priority': 'HIGH',
                'description': f'Found {len(corrupted_files)} files with integrity issues',
                'action': 'Review and potentially remove corrupted files'
            })

        # Schema recommendations
        if schema:
            recommendations.append({
                'type': 'SCHEMA_CONSISTENCY',
                'priority': 'MEDIUM',
                'description': 'Schema inconsistencies detected',
                'action': 'Standardize schemas across files'
            })

        # Quality recommendations
        if quality:
            recommendations.append({
                'type': 'DATA_QUALITY',
                'priority': 'MEDIUM',
                'description': 'Data quality issues detected',
                'action': 'Review data quality metrics and clean data'
            })

        return recommendations

7) Common Pitfalls (The Traps)

File Inspection Mistakes

# ❌ WRONG: Inefficient file inspection
def bad_file_inspection(directory: str):
    # Reading entire files for inspection
    for file_path in glob.glob(f"{directory}/**/*.parquet"):
        df = pd.read_parquet(file_path)  # Loads entire file!
        print(f"File: {file_path}, Rows: {len(df)}")

# ✅ CORRECT: Efficient file inspection
def good_file_inspection(directory: str):
    conn = duckdb.connect()

    # Use DuckDB for efficient inspection
    query = f"""
    SELECT 
        file_name,
        file_size,
        COUNT(*) as row_count
    FROM glob('{directory}/**/*.parquet')
    GROUP BY file_name, file_size
    """

    results = conn.execute(query).fetchall()
    for file_name, file_size, row_count in results:
        print(f"File: {file_name}, Rows: {row_count}")

Memory Management Issues

# ❌ WRONG: Memory-intensive operations
def bad_memory_usage(directory: str):
    conn = duckdb.connect()

    # Loading all files into memory
    all_data = conn.execute(f"SELECT * FROM glob('{directory}/**/*.parquet')").fetchall()
    # This will consume massive amounts of memory!

# ✅ CORRECT: Memory-efficient operations
def good_memory_usage(directory: str):
    conn = duckdb.connect()

    # Process files one at a time
    files = conn.execute(f"SELECT file_name FROM glob('{directory}/**/*.parquet')").fetchall()

    for (file_name,) in files:
        # Process each file individually
        stats = conn.execute(f"""
            SELECT COUNT(*) as row_count
            FROM read_parquet('{file_name}')
        """).fetchone()
        print(f"File: {file_name}, Rows: {stats[0]}")

Why These Pitfalls Matter: Common mistakes lead to poor performance, memory issues, and unreliable results. Understanding these pitfalls prevents costly errors and ensures efficient data quality inspection.

8) Best Practices Checklist (The Wisdom)

Data Quality Inspection Checklist

# ✅ DO: Complete data quality inspection checklist
def data_quality_checklist():
    checklist = {
        "file_integrity": [
            "Check file sizes and corruption",
            "Validate file accessibility",
            "Detect empty or suspiciously small files",
            "Identify potentially corrupted files"
        ],
        "schema_validation": [
            "Validate schema consistency",
            "Detect schema drift",
            "Check column types and nullability",
            "Identify missing or extra columns"
        ],
        "data_quality": [
            "Profile data statistics",
            "Detect null values and completeness",
            "Identify duplicate records",
            "Check data type consistency"
        ],
        "anomaly_detection": [
            "Detect unusual patterns",
            "Identify outliers and anomalies",
            "Check for data quality issues",
            "Validate business rules"
        ]
    }
    return checklist

Performance Optimization Checklist

# ✅ DO: Performance optimization checklist
def performance_optimization_checklist():
    checklist = {
        "connection_optimization": [
            "Set appropriate memory limits",
            "Configure thread count",
            "Enable progress bars",
            "Use connection pooling"
        ],
        "query_optimization": [
            "Use efficient file scanning",
            "Limit data reading to necessary columns",
            "Use appropriate batch sizes",
            "Implement parallel processing"
        ],
        "memory_management": [
            "Process files individually",
            "Use streaming for large files",
            "Implement proper cleanup",
            "Monitor memory usage"
        ],
        "monitoring": [
            "Track processing time",
            "Monitor memory usage",
            "Log errors and issues",
            "Generate performance reports"
        ]
    }
    return checklist

Why These Practices Matter: Best practices ensure successful data quality inspection. Following established patterns prevents common mistakes and enables production-ready data quality systems.

9) TL;DR Quickstart (The Essentials)

Essential DuckDB Setup

# 1) Install DuckDB
pip install duckdb

# 2) Basic file inspection
python -c "
import duckdb
conn = duckdb.connect()
files = conn.execute('SELECT file_name, file_size FROM glob(\"data/*.parquet\")').fetchall()
for file_name, file_size in files:
    print(f'{file_name}: {file_size} bytes')
"

Essential Inspection Queries

-- Basic file information
SELECT 
    file_name,
    file_size,
    file_modified
FROM glob('data/**/*.parquet')
ORDER BY file_size DESC;

-- File integrity check
SELECT 
    file_name,
    file_size,
    CASE 
        WHEN file_size = 0 THEN 'EMPTY'
        WHEN file_size < 1000 THEN 'SUSPICIOUS'
        ELSE 'OK'
    END as status
FROM glob('data/**/*.parquet');

-- Row count per file
SELECT 
    file_name,
    COUNT(*) as row_count
FROM glob('data/**/*.parquet')
GROUP BY file_name;

Essential Python Patterns

# Essential DuckDB patterns for Parquet inspection
def essential_inspection_patterns():
    conn = duckdb.connect()

    # 1. Basic file scanning
    files = conn.execute("SELECT file_name FROM glob('data/*.parquet')").fetchall()

    # 2. File integrity check
    for (file_name,) in files:
        try:
            row_count = conn.execute(f"SELECT COUNT(*) FROM read_parquet('{file_name}')").fetchone()[0]
            print(f"File: {file_name}, Rows: {row_count}")
        except Exception as e:
            print(f"Error reading {file_name}: {e}")

    # 3. Schema validation
    schema = conn.execute("DESCRIBE read_parquet('data/sample.parquet')").fetchall()
    print("Schema:", schema)

Why This Quickstart: These patterns cover 90% of DuckDB Parquet inspection usage. Master these before exploring advanced features.

10) The Machine's Summary

DuckDB provides the foundation for efficient Parquet file inspection and data quality analysis. When used correctly, it enables comprehensive data quality assessment without the overhead of traditional database systems. The key is understanding file validation, mastering data profiling, and following best practices.

The Dark Truth: Without proper DuckDB understanding, your data quality inspection is inefficient and unreliable. DuckDB is your weapon. Use it wisely.

The Machine's Mantra: "In file integrity we trust, in data profiling we build, and in the quality report we find the path to reliable data."

Why This Matters: DuckDB enables applications to efficiently inspect Parquet files and detect data quality issues. It provides the foundation for reliable data pipelines that can identify and handle bad data files automatically.


This guide provides the complete machinery for mastering DuckDB Parquet data quality inspection. The patterns scale from simple file validation to complex data quality pipelines, from basic integrity checks to advanced anomaly detection.