Employee Skills Importer
This skill automates the process of importing employee skills from CSV files into a Supabase database. It parses the CSV, checks what already exists in the database, and generates idempotent SQL scripts to insert missing data.
Overview
The skill performs a 3-step process:
- Identify and insert missing skill categories - Extract categories from CSV headers, check database, generate INSERT script
- Identify and insert missing skills - Extract skills with their categories, check database, generate INSERT script
- Generate employee_skills INSERT script - Map employees by name, link skills, create final INSERT statements
CSV Format Requirements
The CSV must have:
- Row 1: Empty or metadata (ignored)
- Row 2: Skill category names spanning multiple columns
- Row 3+: Individual skill names (column headers, may span multiple rows due to line breaks)
- Employee data rows: Employee data with First Name, Last Name in first two columns, followed by skill experience values
Example structure:
,,,,,,.NET,,,,,Front-end,,,Java,,,
First Name,Last Name,Full Name,Unit,...,C#,ASP.net,MVC,...,JavaScript,HTML,CSS,...,Java,Spring,...
John,Doe,John Doe,Unit 1,...,5,4,3,...,6,6,5,...,0,0,...
Workflow
Step 1: Skill Categories
- Parse row 2 to extract unique category names
- Query the database to check existing categories:
SELECT name FROM skill_categories
- Generate idempotent INSERT for missing categories:
INSERT INTO skill_categories (name)
VALUES ('Category1'), ('Category2'), ('Category3')
ON CONFLICT (name) DO NOTHING;
Step 2: Skills
- Parse skill name rows and map to categories from row 2
- Query database for existing skills:
SELECT s.name, sc.name as category_name
FROM skills s
LEFT JOIN skill_categories sc ON s.category_id = sc.id
- For each skill to insert:
- Find the category_id using a subquery
- Generate idempotent INSERT:
INSERT INTO skills (name, category_id)
VALUES
('C#', (SELECT id FROM skill_categories WHERE name = '.NET')),
('JavaScript', (SELECT id FROM skill_categories WHERE name = 'Front-end'))
ON CONFLICT (name) DO NOTHING;
Step 3: Employee Skills
- Parse employee rows (first_name, last_name, skill values)
- Query employees table to get employee IDs:
SELECT id, first_name, last_name FROM employees
- For each employee, for each skill with non-zero experience:
- Look up employee_id by matching first_name + last_name
- Look up skill_id using subquery
- CRITICAL: Use TRIM() in WHERE clause to handle whitespace variations in database
- Generate INSERT:
INSERT INTO employee_skills (employee_id, skill_id, years_of_experience)
VALUES
(
(SELECT id FROM employees WHERE TRIM(first_name) = 'John' AND TRIM(last_name) = 'Doe'),
(SELECT id FROM skills WHERE name = 'C#'),
5
)
ON CONFLICT (employee_id, skill_id) DO UPDATE
SET years_of_experience = EXCLUDED.years_of_experience;
Important Notes
Database Schema
skill_categories table: id (uuid), name (text, unique)
skills table: id (uuid), name (text, unique), category_id (uuid FK to skill_categories)
employees table: id (uuid), first_name (text), last_name (text)
employee_skills table: id (uuid), employee_id (uuid FK), skill_id (uuid FK), years_of_experience (real)
Idempotency
All generated SQL scripts use ON CONFLICT clauses to ensure they can be run multiple times without errors:
- For categories and skills:
ON CONFLICT (name) DO NOTHING
- For employee_skills:
ON CONFLICT (employee_id, skill_id) DO UPDATE SET years_of_experience = EXCLUDED.years_of_experience
Data Handling
- Skip employees with zero or empty experience values for a skill
- Handle numeric experience values (can be integers or decimals like 0.5, 1.7, etc.)
- Clean up skill names by trimming whitespace and removing line breaks
- Skip rows where employee lookup fails (employee not found in database)
- Handle multi-line CSV cells properly
- CRITICAL: Deduplicate employee-skill pairs before generating SQL - Keep the highest years value when duplicates exist
- CRITICAL: Automatically correct employee name spellings - Use fuzzy matching to find and correct minor spelling differences (e.g., "Victoriia" → "Viktoriia")
- CRITICAL: Trim all employee names - Remove leading/trailing whitespace from all names
- CRITICAL: Use TRIM() in SQL WHERE clauses - Database may have extra spaces (e.g., "Yurii Solokha" with 3 spaces)
- CRITICAL: Skip employees with no match - If no close match found in database, exclude those records and report them
Error Prevention
- Always use subqueries for foreign key lookups rather than hardcoding UUIDs
- Validate that category names match between row 2 and skill lookups
- Report any employees from CSV not found in the database
- Report any skills that couldn't be mapped to categories
CRITICAL - Prevent Duplicate Key Violations:
- Before generating the employee_skills INSERT, deduplicate all records by (first_name, last_name, skill)
- When duplicates exist, keep the record with the highest years_of_experience value
- This prevents:
ON CONFLICT DO UPDATE command cannot affect row a second time
CRITICAL - Automatic Name Correction:
- Before generating SQL, validate ALL employees exist in the database
- For employees not found by exact match:
- Use fuzzy matching (Levenshtein distance or similar) to find close matches in database
- If a close match is found (e.g., "Victoriia" → "Viktoriia"), automatically use the database spelling
- If no close match is found, skip the employee entirely
- Generate a report showing:
- Employees with automatic corrections applied: "CSV name → Database name"
- Employees skipped (no match found): List with number of skills skipped
- This prevents:
null value in column "employee_id" violates not-null constraint
Output Format
The skill produces three SQL scripts plus one report file:
1_insert_categories.sql
-- Insert missing skill categories
INSERT INTO skill_categories (name)
VALUES ('.NET'), ('Front-end'), ('Java')
ON CONFLICT (name) DO NOTHING;
2_insert_skills.sql
-- Insert missing skills with category mapping
INSERT INTO skills (name, category_id)
VALUES
('C#', (SELECT id FROM skill_categories WHERE name = '.NET')),
('ASP.net', (SELECT id FROM skill_categories WHERE name = '.NET')),
('JavaScript', (SELECT id FROM skill_categories WHERE name = 'Front-end'))
ON CONFLICT (name) DO NOTHING;
3_insert_employee_skills.sql
-- Insert employee skills
-- Records have been deduplicated and filtered for valid employees only
-- Using TRIM() in WHERE clause to handle whitespace in database
INSERT INTO employee_skills (employee_id, skill_id, years_of_experience)
VALUES
(
(SELECT id FROM employees WHERE TRIM(first_name) = 'John' AND TRIM(last_name) = 'Doe'),
(SELECT id FROM skills WHERE name = 'C#'),
5
),
(
(SELECT id FROM employees WHERE TRIM(first_name) = 'John' AND TRIM(last_name) = 'Doe'),
(SELECT id FROM skills WHERE name = 'JavaScript'),
6
)
ON CONFLICT (employee_id, skill_id) DO UPDATE
SET years_of_experience = EXCLUDED.years_of_experience;
Execution Steps
When the user provides a CSV file:
Parse the CSV structure
- Read the file and validate format
- Extract category names from row 2
- Extract skill names from subsequent rows (handling multi-line cells)
- Map each skill to its category based on column positions
Query existing data from Supabase
- Fetch all existing skill_categories
- Fetch all existing skills with their categories
- Fetch all employees (id, first_name, last_name)
Generate Script 1: Categories
- Compare CSV categories against database
- Create INSERT statement for missing categories
- Save to file and present to user
Generate Script 2: Skills
- Compare CSV skills against database
- For missing skills, include category lookup subquery
- Create INSERT statement
- Save to file and present to user
Generate Script 3: Employee Skills
- Parse employee rows
- VALIDATE: Compare all CSV employees against database using exact matching
- FUZZY MATCH: For non-exact matches, find closest database employee using similarity algorithm
- Calculate similarity score for first_name and last_name separately
- If combined similarity is above threshold (e.g., 85%), automatically use database name
- Track all automatic corrections for reporting
- CORRECT: Replace CSV names with database names for matched employees
- FILTER: Skip employees with no close match found
- DEDUPLICATE: Remove duplicates by (employee, skill), keeping highest years value
- Generate INSERT statements using corrected employee names
- Generate report showing corrections and skipped employees
- Save SQL file and report to outputs directory
- Present both files to user
Present all files to the user
- Three SQL scripts (1_insert_categories.sql, 2_insert_skills.sql, 3_insert_employee_skills.sql)
- One report file (skipped_employees_report.txt) if any employees were skipped
- User can execute SQL scripts in order: 1 → 2 → 3
- User should review report to fix name mismatches if needed
Usage Example
User uploads CSV file and says:
"Parse this employee skills CSV and generate SQL insert scripts"
Skill responds:
- Analyzes the CSV structure
- Connects to Supabase SkillsSystem project
- Checks existing data in all three tables
- Generates three SQL files
- Reports summary (e.g., "Found 5 new categories, 23 new skills, generating inserts for 47 employees")
- Presents the three SQL files for download
Project Configuration
This skill is configured to work with the Supabase project:
- Project Name: SkillsSystem
- Project ID: ypibfhbklinkvybgotef
- Region: eu-central-1
The skill automatically connects to this project when executing queries.
Common Errors and Solutions
Error 1: "ON CONFLICT DO UPDATE command cannot affect row a second time"
Cause: Duplicate employee-skill pairs in the generated INSERT statement
Solution: The skill now deduplicates all records before generating SQL. If you see this error, it means deduplication was not performed.
Prevention: Always deduplicate by (first_name, last_name, skill) and keep the highest years value
Error 2: "null value in column 'employee_id' violates not-null constraint"
Cause: Employee from CSV not found in database (usually due to name spelling differences or whitespace issues)
Solution: The skill now:
- Automatically corrects spelling differences using fuzzy matching
- Trims all whitespace from names
- Uses TRIM() in SQL WHERE clauses to match database records with extra spaces
Common issues:
- Spelling variations: "Victoriia"↔"Viktoriia", "Karasyov"↔"Karasov"
- Extra whitespace in database: "Yurii Solokha" (3 spaces)
- Leading/trailing spaces
How it works:
- Compares CSV names against database using similarity algorithm
- If close match found (>83% similarity), automatically uses database spelling
- Trims all names before comparison
- Uses
TRIM(first_name) and TRIM(last_name) in SQL to handle database whitespace
- If no close match found, skips the employee and reports it
Result: This error should no longer occur as names are automatically corrected and whitespace is handled
Name Matching Algorithm
The skill uses the following approach:
- Try exact match first (first_name AND last_name)
- If no exact match, calculate similarity score using:
- Levenshtein distance or similar algorithm
- Handles common variations: "Victoriia"↔"Viktoriia", "Karasyov"↔"Karasov"
- If similarity > 83% threshold, accept as match
- If multiple close matches found, pick the closest one
- If no close match, skip the employee
- Always trim whitespace from both CSV and database names
- Use TRIM() in SQL queries to match records with extra spaces in database
1---2name: employee-skills-importer3description: Parse employee skills CSV files, identify skill categories and individual skills, look up employee IDs from an employees table, and generate idempotent SQL INSERT statements for skill_categories, skills, and employee_skills tables.4---56# Employee Skills Importer78This skill automates the process of importing employee skills from CSV files into a Supabase database. It parses the CSV, checks what already exists in the database, and generates idempotent SQL scripts to insert missing data.910## Overview1112The skill performs a 3-step process:131. **Identify and insert missing skill categories** - Extract categories from CSV headers, check database, generate INSERT script142. **Identify and insert missing skills** - Extract skills with their categories, check database, generate INSERT script 153. **Generate employee_skills INSERT script** - Map employees by name, link skills, create final INSERT statements1617## CSV Format Requirements1819The CSV must have:20- **Row 1**: Empty or metadata (ignored)21- **Row 2**: Skill category names spanning multiple columns22- **Row 3+**: Individual skill names (column headers, may span multiple rows due to line breaks)23- **Employee data rows**: Employee data with First Name, Last Name in first two columns, followed by skill experience values2425Example structure:26```27,,,,,,.NET,,,,,Front-end,,,Java,,,28First Name,Last Name,Full Name,Unit,...,C#,ASP.net,MVC,...,JavaScript,HTML,CSS,...,Java,Spring,...29John,Doe,John Doe,Unit 1,...,5,4,3,...,6,6,5,...,0,0,...30```3132## Workflow3334### Step 1: Skill Categories35361. Parse row 2 to extract unique category names372. Query the database to check existing categories:38 ```sql39 SELECT name FROM skill_categories40 ```413. Generate idempotent INSERT for missing categories:42 ```sql43 INSERT INTO skill_categories (name) 44 VALUES ('Category1'), ('Category2'), ('Category3')45 ON CONFLICT (name) DO NOTHING;46 ```4748### Step 2: Skills49501. Parse skill name rows and map to categories from row 2512. Query database for existing skills:52 ```sql53 SELECT s.name, sc.name as category_name 54 FROM skills s 55 LEFT JOIN skill_categories sc ON s.category_id = sc.id56 ```573. For each skill to insert:58 - Find the category_id using a subquery59 - Generate idempotent INSERT:60 ```sql61 INSERT INTO skills (name, category_id)62 VALUES 63 ('C#', (SELECT id FROM skill_categories WHERE name = '.NET')),64 ('JavaScript', (SELECT id FROM skill_categories WHERE name = 'Front-end'))65 ON CONFLICT (name) DO NOTHING;66 ```6768### Step 3: Employee Skills69701. Parse employee rows (first_name, last_name, skill values)712. Query employees table to get employee IDs:72 ```sql73 SELECT id, first_name, last_name FROM employees74 ```753. For each employee, for each skill with non-zero experience:76 - Look up employee_id by matching first_name + last_name77 - Look up skill_id using subquery78 - **CRITICAL: Use TRIM() in WHERE clause to handle whitespace variations in database**79 - Generate INSERT:80 ```sql81 INSERT INTO employee_skills (employee_id, skill_id, years_of_experience)82 VALUES 83 (84 (SELECT id FROM employees WHERE TRIM(first_name) = 'John' AND TRIM(last_name) = 'Doe'),85 (SELECT id FROM skills WHERE name = 'C#'),86 587 )88 ON CONFLICT (employee_id, skill_id) DO UPDATE 89 SET years_of_experience = EXCLUDED.years_of_experience;90 ```9192## Important Notes9394### Database Schema95- `skill_categories` table: id (uuid), name (text, unique)96- `skills` table: id (uuid), name (text, unique), category_id (uuid FK to skill_categories)97- `employees` table: id (uuid), first_name (text), last_name (text)98- `employee_skills` table: id (uuid), employee_id (uuid FK), skill_id (uuid FK), years_of_experience (real)99100### Idempotency101All generated SQL scripts use `ON CONFLICT` clauses to ensure they can be run multiple times without errors:102- For categories and skills: `ON CONFLICT (name) DO NOTHING`103- For employee_skills: `ON CONFLICT (employee_id, skill_id) DO UPDATE SET years_of_experience = EXCLUDED.years_of_experience`104105### Data Handling106- Skip employees with zero or empty experience values for a skill107- Handle numeric experience values (can be integers or decimals like 0.5, 1.7, etc.)108- Clean up skill names by trimming whitespace and removing line breaks109- Skip rows where employee lookup fails (employee not found in database)110- Handle multi-line CSV cells properly111- **CRITICAL: Deduplicate employee-skill pairs before generating SQL** - Keep the highest years value when duplicates exist112- **CRITICAL: Automatically correct employee name spellings** - Use fuzzy matching to find and correct minor spelling differences (e.g., "Victoriia" → "Viktoriia")113- **CRITICAL: Trim all employee names** - Remove leading/trailing whitespace from all names114- **CRITICAL: Use TRIM() in SQL WHERE clauses** - Database may have extra spaces (e.g., "Yurii Solokha" with 3 spaces)115- **CRITICAL: Skip employees with no match** - If no close match found in database, exclude those records and report them116117### Error Prevention118- Always use subqueries for foreign key lookups rather than hardcoding UUIDs119- Validate that category names match between row 2 and skill lookups120- Report any employees from CSV not found in the database121- Report any skills that couldn't be mapped to categories122123**CRITICAL - Prevent Duplicate Key Violations:**1241. Before generating the employee_skills INSERT, deduplicate all records by (first_name, last_name, skill)1252. When duplicates exist, keep the record with the highest years_of_experience value1263. This prevents: `ON CONFLICT DO UPDATE command cannot affect row a second time`127128**CRITICAL - Automatic Name Correction:**1291. Before generating SQL, validate ALL employees exist in the database1302. For employees not found by exact match:131 - Use fuzzy matching (Levenshtein distance or similar) to find close matches in database132 - If a close match is found (e.g., "Victoriia" → "Viktoriia"), automatically use the database spelling133 - If no close match is found, skip the employee entirely1343. Generate a report showing:135 - Employees with automatic corrections applied: "CSV name → Database name"136 - Employees skipped (no match found): List with number of skills skipped1374. This prevents: `null value in column "employee_id" violates not-null constraint`138139## Output Format140141The skill produces three SQL scripts plus one report file:142143**1_insert_categories.sql**144```sql145-- Insert missing skill categories146INSERT INTO skill_categories (name) 147VALUES ('.NET'), ('Front-end'), ('Java')148ON CONFLICT (name) DO NOTHING;149```150151**2_insert_skills.sql**152```sql153-- Insert missing skills with category mapping154INSERT INTO skills (name, category_id)155VALUES 156 ('C#', (SELECT id FROM skill_categories WHERE name = '.NET')),157 ('ASP.net', (SELECT id FROM skill_categories WHERE name = '.NET')),158 ('JavaScript', (SELECT id FROM skill_categories WHERE name = 'Front-end'))159ON CONFLICT (name) DO NOTHING;160```161162**3_insert_employee_skills.sql**163```sql164-- Insert employee skills165-- Records have been deduplicated and filtered for valid employees only166-- Using TRIM() in WHERE clause to handle whitespace in database167INSERT INTO employee_skills (employee_id, skill_id, years_of_experience)168VALUES 169 (170 (SELECT id FROM employees WHERE TRIM(first_name) = 'John' AND TRIM(last_name) = 'Doe'),171 (SELECT id FROM skills WHERE name = 'C#'),172 5173 ),174 (175 (SELECT id FROM employees WHERE TRIM(first_name) = 'John' AND TRIM(last_name) = 'Doe'),176 (SELECT id FROM skills WHERE name = 'JavaScript'),177 6178 )179ON CONFLICT (employee_id, skill_id) DO UPDATE 180SET years_of_experience = EXCLUDED.years_of_experience;181```182183## Execution Steps184185When the user provides a CSV file:1861871. **Parse the CSV structure**188 - Read the file and validate format189 - Extract category names from row 2190 - Extract skill names from subsequent rows (handling multi-line cells)191 - Map each skill to its category based on column positions1921932. **Query existing data from Supabase**194 - Fetch all existing skill_categories195 - Fetch all existing skills with their categories196 - Fetch all employees (id, first_name, last_name)1971983. **Generate Script 1: Categories**199 - Compare CSV categories against database200 - Create INSERT statement for missing categories201 - Save to file and present to user2022034. **Generate Script 2: Skills**204 - Compare CSV skills against database205 - For missing skills, include category lookup subquery206 - Create INSERT statement207 - Save to file and present to user2082095. **Generate Script 3: Employee Skills**210 - Parse employee rows211 - **VALIDATE: Compare all CSV employees against database using exact matching**212 - **FUZZY MATCH: For non-exact matches, find closest database employee using similarity algorithm**213 - Calculate similarity score for first_name and last_name separately214 - If combined similarity is above threshold (e.g., 85%), automatically use database name215 - Track all automatic corrections for reporting216 - **CORRECT: Replace CSV names with database names for matched employees**217 - **FILTER: Skip employees with no close match found**218 - **DEDUPLICATE: Remove duplicates by (employee, skill), keeping highest years value**219 - Generate INSERT statements using corrected employee names220 - **Generate report showing corrections and skipped employees**221 - Save SQL file and report to outputs directory222 - Present both files to user2232246. **Present all files to the user**225 - Three SQL scripts (1_insert_categories.sql, 2_insert_skills.sql, 3_insert_employee_skills.sql)226 - One report file (skipped_employees_report.txt) if any employees were skipped227 - User can execute SQL scripts in order: 1 → 2 → 3228 - User should review report to fix name mismatches if needed229230## Usage Example231232User uploads CSV file and says:233"Parse this employee skills CSV and generate SQL insert scripts"234235Skill responds:2361. Analyzes the CSV structure2372. Connects to Supabase SkillsSystem project2383. Checks existing data in all three tables2394. Generates three SQL files2405. Reports summary (e.g., "Found 5 new categories, 23 new skills, generating inserts for 47 employees")2416. Presents the three SQL files for download242243## Project Configuration244245This skill is configured to work with the Supabase project:246- **Project Name**: SkillsSystem 247- **Project ID**: ypibfhbklinkvybgotef248- **Region**: eu-central-1249250The skill automatically connects to this project when executing queries.251252## Common Errors and Solutions253254### Error 1: "ON CONFLICT DO UPDATE command cannot affect row a second time"255**Cause:** Duplicate employee-skill pairs in the generated INSERT statement256**Solution:** The skill now deduplicates all records before generating SQL. If you see this error, it means deduplication was not performed.257**Prevention:** Always deduplicate by (first_name, last_name, skill) and keep the highest years value258259### Error 2: "null value in column 'employee_id' violates not-null constraint"260**Cause:** Employee from CSV not found in database (usually due to name spelling differences or whitespace issues)261**Solution:** The skill now:2621. Automatically corrects spelling differences using fuzzy matching2632. Trims all whitespace from names2643. Uses TRIM() in SQL WHERE clauses to match database records with extra spaces265266**Common issues:**267- Spelling variations: "Victoriia"↔"Viktoriia", "Karasyov"↔"Karasov"268- Extra whitespace in database: "Yurii Solokha" (3 spaces)269- Leading/trailing spaces270271**How it works:**272- Compares CSV names against database using similarity algorithm273- If close match found (>83% similarity), automatically uses database spelling274- Trims all names before comparison275- Uses `TRIM(first_name)` and `TRIM(last_name)` in SQL to handle database whitespace276- If no close match found, skips the employee and reports it277278**Result:** This error should no longer occur as names are automatically corrected and whitespace is handled279280### Name Matching Algorithm281The skill uses the following approach:2821. Try exact match first (first_name AND last_name)2832. If no exact match, calculate similarity score using:284 - Levenshtein distance or similar algorithm285 - Handles common variations: "Victoriia"↔"Viktoriia", "Karasyov"↔"Karasov"2863. If similarity > 83% threshold, accept as match2874. If multiple close matches found, pick the closest one2885. If no close match, skip the employee2896. Always trim whitespace from both CSV and database names2907. Use TRIM() in SQL queries to match records with extra spaces in database