PostgreSQL - La base de datos de código abierto más avanzada del mundo
Postgre SQL es un sistema de base de datos de código abierto con más de 30 años de desarrollo activo. Conocido por su fiabilidad, su robustez y rendimiento, soporta tanto la búsqueda SQL como JSON.
# Using Homebrewbrewinstallpostgresql
# Start PostgreSQLbrewservicesstartpostgresql
# Create databasecreatedbmydatabase
# Connect to PostgreSQLpsqlmydatabase
# Using PostgreSQL.app# Download from https://postgresapp.com/
# Download installer from https://www.postgresql.org/download/windows/# Run the installer and follow the setup wizard# Connect using psqlpsql-Upostgres-hlocalhost
# Or use pgAdmin GUI tool
# Pull PostgreSQL imagedockerpullpostgres:15
# Run PostgreSQL containerdockerrun--namepostgres-container\-ePOSTGRES_PASSWORD=mypassword\-ePOSTGRES_DB=mydatabase\-p5432:5432\-dpostgres:15
# Connect to PostgreSQL in containerdockerexec-itpostgres-containerpsql-Upostgres-dmydatabase
# Run with persistent datadockerrun--namepostgres-container\-ePOSTGRES_PASSWORD=mypassword\-ePOSTGRES_DB=mydatabase\-p5432:5432\-vpostgres-data:/var/lib/postgresql/data\-dpostgres:15
-- Connect to PostgreSQLpsql-Uusername-ddatabase_name-- Connect to remote serverpsql-hhostname-p5432-Uusername-ddatabase_name-- Connect with SSLpsql"host=hostname port=5432 dbname=database_name user=username sslmode=require"-- Connect and execute commandpsql-Uusername-ddatabase_name-c"SELECT version();"-- Connect from filepsql-Uusername-ddatabase_name-fscript.sql
-- Show PostgreSQL versionSELECTversion();-- Show current userSELECTcurrent_user;-- Show current databaseSELECTcurrent_database();-- Show current date and timeSELECTnow();-- Show server settingsSHOWALL;-- Show specific settingSHOWshared_buffers;-- Show active connectionsSELECT*FROMpg_stat_activity;-- Show database sizeSELECTpg_size_pretty(pg_database_size(current_database()));
-- List databases\l-- Connect to database\cdatabase_name-- List tables\dt-- List all relations (tables, views, sequences)\d-- Describe table\dtable_name-- List schemas\dn-- List users/roles\du-- List functions\df-- Show table sizes\dt+-- Execute system command\!ls-la-- Quit psql\q-- Help\?-- SQL help\hSELECT
-- Create databaseCREATEDATABASEmydatabase;-- Create database with optionsCREATEDATABASEmydatabaseWITHOWNER=myuserENCODING='UTF8'LC_COLLATE='en_US.UTF-8'LC_CTYPE='en_US.UTF-8'TEMPLATE=template0;-- Create database with specific tablespaceCREATEDATABASEmydatabaseWITHTABLESPACE=my_tablespace;-- Create database if not exists (PostgreSQL 9.1+)CREATEDATABASEIFNOTEXISTSmydatabase;
-- List all databases\lSELECTdatnameFROMpg_database;-- Connect to database\cmydatabase-- Show current databaseSELECTcurrent_database();-- Rename databaseALTERDATABASEold_nameRENAMETOnew_name;-- Change database ownerALTERDATABASEmydatabaseOWNERTOnew_owner;-- Drop databaseDROPDATABASEmydatabase;-- Drop database if existsDROPDATABASEIFEXISTSmydatabase;-- Terminate connections to databaseSELECTpg_terminate_backend(pg_stat_activity.pid)FROMpg_stat_activityWHEREpg_stat_activity.datname='mydatabase'ANDpid<>pg_backend_pid();
-- Show database sizeSELECTdatnameasdatabase_name,pg_size_pretty(pg_database_size(datname))assizeFROMpg_databaseORDERBYpg_database_size(datname)DESC;-- Show table sizes in current databaseSELECTschemaname,tablename,pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename))assize,pg_size_pretty(pg_relation_size(schemaname||'.'||tablename))astable_size,pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)-pg_relation_size(schemaname||'.'||tablename))asindex_sizeFROMpg_tablesORDERBYpg_total_relation_size(schemaname||'.'||tablename)DESC;-- Show database connectionsSELECTdatname,numbackends,xact_commit,xact_rollback,blks_read,blks_hitFROMpg_stat_database;
-- Basic table creationCREATETABLEusers(idSERIALPRIMARYKEY,usernameVARCHAR(50)NOTNULLUNIQUE,emailVARCHAR(100)NOTNULL,passwordVARCHAR(255)NOTNULL,created_atTIMESTAMPDEFAULTCURRENT_TIMESTAMP);-- Table with various data typesCREATETABLEproducts(idSERIALPRIMARYKEY,nameVARCHAR(100)NOTNULL,descriptionTEXT,priceDECIMAL(10,2)NOTNULLCHECK(price>0),category_idINTEGERREFERENCEScategories(id),tagsTEXT[],metadataJSONB,is_activeBOOLEANDEFAULTTRUE,created_atTIMESTAMPWITHTIMEZONEDEFAULTNOW(),updated_atTIMESTAMPWITHTIMEZONEDEFAULTNOW());-- Table with constraintsCREATETABLEorders(idSERIALPRIMARYKEY,user_idINTEGERNOTNULLREFERENCESusers(id)ONDELETECASCADE,order_dateDATENOTNULLDEFAULTCURRENT_DATE,total_amountDECIMAL(10,2)NOTNULL,statusVARCHAR(20)NOTNULLDEFAULT'pending',CONSTRAINTvalid_statusCHECK(statusIN('pending','processing','shipped','delivered','cancelled')));-- Temporary tableCREATETEMPORARYTABLEtemp_data(idINTEGER,valueTEXT);-- Table with inheritanceCREATETABLEvehicles(idSERIALPRIMARYKEY,makeVARCHAR(50),modelVARCHAR(50),yearINTEGER);CREATETABLEcars(doorsINTEGER)INHERITS(vehicles);
-- Describe table structure\dusers\d+users-- with additional info-- Show table columnsSELECTcolumn_name,data_type,is_nullable,column_defaultFROMinformation_schema.columnsWHEREtable_name='users';-- Show table constraintsSELECTconstraint_name,constraint_type,column_nameFROMinformation_schema.table_constraintstcJOINinformation_schema.constraint_column_usageccuONtc.constraint_name=ccu.constraint_nameWHEREtc.table_name='users';-- Show table indexesSELECTindexname,indexdefFROMpg_indexesWHEREtablename='users';-- Show foreign keysSELECTtc.constraint_name,tc.table_name,kcu.column_name,ccu.table_nameASforeign_table_name,ccu.column_nameASforeign_column_nameFROMinformation_schema.table_constraintsAStcJOINinformation_schema.key_column_usageASkcuONtc.constraint_name=kcu.constraint_nameJOINinformation_schema.constraint_column_usageASccuONccu.constraint_name=tc.constraint_nameWHEREtc.constraint_type='FOREIGN KEY'ANDtc.table_name='users';
-- Drop tableDROPTABLEtable_name;-- Drop table if existsDROPTABLEIFEXISTStable_name;-- Drop multiple tablesDROPTABLEtable1,table2,table3;-- Drop table cascade (remove dependent objects)DROPTABLEtable_nameCASCADE;-- Truncate table (delete all data, keep structure)TRUNCATETABLEtable_name;-- Truncate with cascadeTRUNCATETABLEtable_nameCASCADE;-- Truncate and restart identityTRUNCATETABLEtable_nameRESTARTIDENTITY;
-- Character typesCHAR(n)-- Fixed-length character stringVARCHAR(n)-- Variable-length character stringTEXT-- Variable-length character string (unlimited)-- ExamplesCREATETABLEtext_examples(idSERIALPRIMARYKEY,codeCHAR(5),-- Fixed 5 charactersnameVARCHAR(100),-- Up to 100 charactersdescriptionTEXT-- Unlimited length);
-- Date and time typesDATE-- Date only (YYYY-MM-DD)TIME-- Time only (HH:MM:SS)TIMEWITHTIMEZONE-- Time with timezoneTIMESTAMP-- Date and timeTIMESTAMPWITHTIMEZONE-- Date and time with timezoneINTERVAL-- Time interval-- ExamplesCREATETABLEdatetime_examples(idSERIALPRIMARYKEY,birth_dateDATE,meeting_timeTIME,created_atTIMESTAMPDEFAULTNOW(),updated_atTIMESTAMPWITHTIMEZONEDEFAULTNOW(),durationINTERVAL);-- Insert examplesINSERTINTOdatetime_examples(birth_date,meeting_time,duration)VALUES('1990-05-15','14:30:00','2 hours 30 minutes');
-- Basic insertINSERTINTOusers(username,email,password)VALUES('john_doe','john@example.com','hashed_password');-- Insert multiple rowsINSERTINTOusers(username,email,password)VALUES('alice','alice@example.com','password1'),('bob','bob@example.com','password2'),('charlie','charlie@example.com','password3');-- Insert with returningINSERTINTOusers(username,email,password)VALUES('dave','dave@example.com','password4')RETURNINGid,username,created_at;-- Insert from another tableINSERTINTOusers_backup(username,email)SELECTusername,emailFROMusersWHEREcreated_at<'2023-01-01';-- Insert with ON CONFLICT (upsert)INSERTINTOusers(id,username,email)VALUES(1,'john_doe','newemail@example.com')ONCONFLICT(id)DOUPDATESETemail=EXCLUDED.email,updated_at=NOW();-- Insert with DO NOTHING on conflictINSERTINTOusers(username,email,password)VALUES('existing_user','existing@example.com','password')ONCONFLICT(username)DONOTHING;-- Insert array dataINSERTINTOproducts(name,tags)VALUES('Product 1',ARRAY['electronics','gadget','new']);-- Insert JSON dataINSERTINTOuser_preferences(user_id,preferences)VALUES(1,'{"theme": "dark", "notifications": true}');
-- Basic selectSELECT*FROMusers;-- Select specific columnsSELECTusername,emailFROMusers;-- Select with aliasSELECTusernameASuser_name,emailASuser_emailFROMusers;-- Select with WHERE clauseSELECT*FROMusersWHEREid=1;SELECT*FROMusersWHEREusername='john_doe';SELECT*FROMusersWHEREcreated_at>'2023-01-01';-- Select with multiple conditionsSELECT*FROMusersWHEREusername='john_doe'ANDemail='john@example.com';SELECT*FROMusersWHEREidIN(1,2,3);SELECT*FROMusersWHEREusernameLIKE'john%';SELECT*FROMusersWHEREemailISNOTNULL;-- Select with ORDER BYSELECT*FROMusersORDERBYcreated_atDESC;SELECT*FROMusersORDERBYusernameASC,created_atDESC;-- Select with LIMIT and OFFSETSELECT*FROMusersLIMIT10;SELECT*FROMusersLIMIT10OFFSET20;SELECT*FROMusersORDERBYidLIMIT5;-- Select with GROUP BYSELECTCOUNT(*)asuser_countFROMusers;SELECTDATE(created_at)asdate,COUNT(*)asdaily_usersFROMusersGROUPBYDATE(created_at);-- Select with HAVINGSELECTDATE(created_at)asdate,COUNT(*)asdaily_usersFROMusersGROUPBYDATE(created_at)HAVINGCOUNT(*)>5;-- Select with window functionsSELECTusername,created_at,ROW_NUMBER()OVER(ORDERBYcreated_at)asrow_num,RANK()OVER(ORDERBYcreated_at)asrank,LAG(username)OVER(ORDERBYcreated_at)asprev_userFROMusers;-- Select with CTE (Common Table Expression)WITHrecent_usersAS(SELECT*FROMusersWHEREcreated_at>'2023-01-01')SELECTCOUNT(*)FROMrecent_users;-- Recursive CTEWITHRECURSIVEemployee_hierarchyAS(SELECTid,name,manager_id,1aslevelFROMemployeesWHEREmanager_idISNULLUNIONALLSELECTe.id,e.name,e.manager_id,eh.level+1FROMemployeeseJOINemployee_hierarchyehONe.manager_id=eh.id)SELECT*FROMemployee_hierarchy;
-- Basic deleteDELETEFROMusersWHEREid=1;-- Delete with conditionsDELETEFROMusersWHEREusername='john_doe';DELETEFROMusersWHEREcreated_at<'2022-01-01';-- Delete multiple rowsDELETEFROMusersWHEREidIN(1,2,3);-- Delete with USING clauseDELETEFROMusersUSINGuser_profilespWHEREusers.id=p.user_idANDp.is_deleted=TRUE;-- Delete with subqueryDELETEFROMusersWHEREidIN(SELECTuser_idFROMinactive_users);-- Delete with RETURNINGDELETEFROMusersWHEREid=1RETURNINGid,username,email;-- Delete all rows (keep table structure)DELETEFROMusers;-- Truncate table (faster for deleting all rows)TRUNCATETABLEusers;TRUNCATETABLEusersRESTARTIDENTITY;
-- Create index on single columnCREATEINDEXidx_users_usernameONusers(username);-- Create index on multiple columnsCREATEINDEXidx_users_name_emailONusers(username,email);-- Create unique indexCREATEUNIQUEINDEXidx_users_emailONusers(email);-- Create partial indexCREATEINDEXidx_active_usersONusers(username)WHEREis_active=TRUE;-- Create expression indexCREATEINDEXidx_users_lower_emailONusers(LOWER(email));-- Create index with specific methodCREATEINDEXidx_users_username_hashONusersUSINGHASH(username);-- Create index concurrently (doesn't block writes)CREATEINDEXCONCURRENTLYidx_users_created_atONusers(created_at);-- Create GIN index for arraysCREATEINDEXidx_products_tagsONproductsUSINGGIN(tags);-- Create GiST index for full-text searchCREATEINDEXidx_posts_contentONpostsUSINGGiST(to_tsvector('english',content));-- Create BRIN index for large tablesCREATEINDEXidx_logs_timestampONlogsUSINGBRIN(timestamp);
-- List indexes for table\dusersSELECTindexname,indexdefFROMpg_indexesWHEREtablename='users';-- Show index usage statisticsSELECTschemaname,tablename,indexname,idx_tup_read,idx_tup_fetchFROMpg_stat_user_indexesWHEREtablename='users';-- Show index sizeSELECTindexname,pg_size_pretty(pg_relation_size(indexname::regclass))assizeFROMpg_indexesWHEREtablename='users';-- Drop indexDROPINDEXidx_users_username;-- Drop index concurrentlyDROPINDEXCONCURRENTLYidx_users_username;-- ReindexREINDEXINDEXidx_users_username;REINDEXTABLEusers;REINDEXDATABASEmydatabase;-- Analyze index usageEXPLAIN(ANALYZE,BUFFERS)SELECT*FROMusersWHEREusername='john_doe';
-- B-tree index (default)CREATEINDEXidx_btreeONusers(username);-- Hash index (for equality comparisons)CREATEINDEXidx_hashONusers(id)USINGHASH;-- GIN index (for arrays, JSONB, full-text search)CREATEINDEXidx_gin_tagsONproductsUSINGGIN(tags);CREATEINDEXidx_gin_jsonbONproductsUSINGGIN(metadata);-- GiST index (for geometric data, full-text search)CREATEINDEXidx_gist_locationONlocationsUSINGGiST(coordinates);-- SP-GiST index (for non-balanced data structures)CREATEINDEXidx_spgistONtable_nameUSINGSPGIST(column_name);-- BRIN index (for very large tables with natural ordering)CREATEINDEXidx_brin_timestampONlarge_tableUSINGBRIN(timestamp);-- Covering index (includes additional columns)CREATEINDEXidx_coveringONusers(username)INCLUDE(email,created_at);
-- Add NOT NULL during table creationCREATETABLEusers(idSERIALPRIMARYKEY,usernameVARCHAR(50)NOTNULL,emailVARCHAR(100)NOTNULL);-- Add NOT NULL to existing columnALTERTABLEusersALTERCOLUMNemailSETNOTNULL;-- Remove NOT NULL constraintALTERTABLEusersALTERCOLUMNphoneDROPNOTNULL;-- Add column with NOT NULL and defaultALTERTABLEusersADDCOLUMNstatusVARCHAR(20)NOTNULLDEFAULT'active';
-- ROW_NUMBER, RANK, DENSE_RANKSELECTusername,created_at,ROW_NUMBER()OVER(ORDERBYcreated_at)asrow_num,RANK()OVER(ORDERBYcreated_at)asrank,DENSE_RANK()OVER(ORDERBYcreated_at)asdense_rankFROMusers;-- Partition bySELECTusername,department,salary,ROW_NUMBER()OVER(PARTITIONBYdepartmentORDERBYsalaryDESC)asdept_rank,AVG(salary)OVER(PARTITIONBYdepartment)asdept_avg_salaryFROMemployees;-- LAG and LEADSELECTusername,created_at,LAG(created_at)OVER(ORDERBYcreated_at)asprev_created_at,LEAD(created_at)OVER(ORDERBYcreated_at)asnext_created_atFROMusers;-- FIRST_VALUE and LAST_VALUESELECTusername,salary,FIRST_VALUE(salary)OVER(ORDERBYsalaryDESC)ashighest_salary,LAST_VALUE(salary)OVER(ORDERBYsalaryDESCROWSBETWEENUNBOUNDEDPRECEDINGANDUNBOUNDEDFOLLOWING)aslowest_salaryFROMemployees;-- NTILE (divide into buckets)SELECTusername,salary,NTILE(4)OVER(ORDERBYsalary)assalary_quartileFROMemployees;-- Cumulative sumSELECTorder_date,amount,SUM(amount)OVER(ORDERBYorder_date)asrunning_totalFROMorders;
-- LATERAL join (correlated subquery in FROM clause)SELECTu.username,recent_posts.title,recent_posts.created_atFROMusersuCROSSJOINLATERAL(SELECTtitle,created_atFROMpostspWHEREp.user_id=u.idORDERBYcreated_atDESCLIMIT3)recent_posts;-- LATERAL with functionSELECTu.username,user_stats.post_count,user_stats.avg_post_lengthFROMusersuCROSSJOINLATERAL(SELECTCOUNT(*)aspost_count,AVG(LENGTH(content))asavg_post_lengthFROMpostspWHEREp.user_id=u.id)user_stats;
-- Simple CASESELECTusername,CASEstatusWHEN'active'THEN'User is active'WHEN'inactive'THEN'User is inactive'WHEN'banned'THEN'User is banned'ELSE'Unknown status'ENDasstatus_descriptionFROMusers;-- Searched CASESELECTusername,salary,CASEWHENsalary<30000THEN'Low'WHENsalary<60000THEN'Medium'WHENsalary<100000THEN'High'ELSE'Very High'ENDassalary_categoryFROMemployees;-- CASE in aggregateSELECTdepartment,COUNT(*)astotal_employees,COUNT(CASEWHENsalary>50000THEN1END)ashigh_earners,COUNT(CASEWHENsalary<=50000THEN1END)aslow_earnersFROMemployeesGROUPBYdepartment;
-- Simple functionCREATEORREPLACEFUNCTIONget_user_count()RETURNSINTEGERAS$$BEGINRETURN(SELECTCOUNT(*)FROMusers);END;$$LANGUAGEplpgsql;-- Function with parametersCREATEORREPLACEFUNCTIONget_user_by_id(user_idINTEGER)RETURNSTABLE(idINTEGER,usernameVARCHAR,emailVARCHAR)AS$$BEGINRETURNQUERYSELECTu.id,u.username,u.emailFROMusersuWHEREu.id=user_id;END;$$LANGUAGEplpgsql;-- Function with default parametersCREATEORREPLACEFUNCTIONget_users_by_status(user_statusVARCHARDEFAULT'active',limit_countINTEGERDEFAULT10)RETURNSTABLE(idINTEGER,usernameVARCHAR,statusVARCHAR)AS$$BEGINRETURNQUERYSELECTu.id,u.username,u.statusFROMusersuWHEREu.status=user_statusLIMITlimit_count;END;$$LANGUAGEplpgsql;-- Function with OUT parametersCREATEORREPLACEFUNCTIONget_user_stats(OUTtotal_usersINTEGER,OUTactive_usersINTEGER,OUTinactive_usersINTEGER)AS$$BEGINSELECTCOUNT(*)INTOtotal_usersFROMusers;SELECTCOUNT(*)INTOactive_usersFROMusersWHEREis_active=TRUE;SELECTCOUNT(*)INTOinactive_usersFROMusersWHEREis_active=FALSE;END;$$LANGUAGEplpgsql;-- Function with exception handlingCREATEORREPLACEFUNCTIONsafe_divide(aNUMERIC,bNUMERIC)RETURNSNUMERICAS$$BEGINIFb=0THENRAISEEXCEPTION'Division by zero';ENDIF;RETURNa/b;EXCEPTIONWHENdivision_by_zeroTHENRAISENOTICE'Cannot divide by zero, returning NULL';RETURNNULL;END;$$LANGUAGEplpgsql;```_###Procedimientosalmacenados```sql-- Simple procedureCREATEORREPLACEPROCEDUREupdate_user_status(user_idINTEGER,new_statusVARCHAR)AS$$BEGINUPDATEusersSETstatus=new_statusWHEREid=user_id;IFNOTFOUNDTHENRAISEEXCEPTION'User with id % not found',user_id;ENDIF;COMMIT;END;$$LANGUAGEplpgsql;-- Procedure with transaction controlCREATEORREPLACEPROCEDUREtransfer_funds(from_accountINTEGER,to_accountINTEGER,amountDECIMAL)AS$$DECLAREfrom_balanceDECIMAL;BEGIN-- Start transactionBEGIN-- Check balanceSELECTbalanceINTOfrom_balanceFROMaccountsWHEREid=from_accountFORUPDATE;IFfrom_balance<amountTHENRAISEEXCEPTION'Insufficient funds';ENDIF;-- Debit from source accountUPDATEaccountsSETbalance=balance-amountWHEREid=from_account;-- Credit to destination accountUPDATEaccountsSETbalance=balance+amountWHEREid=to_account;-- Log transactionINSERTINTOtransactions(from_account,to_account,amount,timestamp)VALUES(from_account,to_account,amount,NOW());COMMIT;RAISENOTICE'Transfer completed successfully';EXCEPTIONWHENOTHERSTHENROLLBACK;RAISEEXCEPTION'Transfer failed: %',SQLERRM;END;END;$$LANGUAGEplpgsql;-- Call proceduresCALLupdate_user_status(1,'inactive');CALLtransfer_funds(100,200,500.00);
-- List functions\dfSELECTroutine_name,routine_typeFROMinformation_schema.routinesWHEREroutine_schema='public';-- Show function definition\df+function_nameSELECTpg_get_functiondef(oid)FROMpg_procWHEREproname='function_name';-- Drop functionDROPFUNCTIONIFEXISTSget_user_count();-- Drop procedureDROPPROCEDUREIFEXISTSupdate_user_status(INTEGER,VARCHAR);-- Call functionSELECTget_user_count();SELECT*FROMget_user_by_id(1);-- Call function with OUT parametersSELECT*FROMget_user_stats();
-- Create trigger functionCREATEORREPLACEFUNCTIONupdate_modified_column()RETURNSTRIGGERAS$$BEGINNEW.updated_at=NOW();RETURNNEW;END;$$LANGUAGEplpgsql;-- Create triggerCREATETRIGGERupdate_users_updated_atBEFOREUPDATEONusersFOREACHROWEXECUTEFUNCTIONupdate_modified_column();-- BEFORE INSERT triggerCREATEORREPLACEFUNCTIONbefore_user_insert()RETURNSTRIGGERAS$$BEGINNEW.created_at=NOW();NEW.username=LOWER(NEW.username);RETURNNEW;END;$$LANGUAGEplpgsql;CREATETRIGGERbefore_user_insert_triggerBEFOREINSERTONusersFOREACHROWEXECUTEFUNCTIONbefore_user_insert();-- AFTER INSERT triggerCREATEORREPLACEFUNCTIONafter_user_insert()RETURNSTRIGGERAS$$BEGININSERTINTOuser_audit(user_id,action,timestamp)VALUES(NEW.id,'INSERT',NOW());RETURNNEW;END;$$LANGUAGEplpgsql;CREATETRIGGERafter_user_insert_triggerAFTERINSERTONusersFOREACHROWEXECUTEFUNCTIONafter_user_insert();-- BEFORE UPDATE trigger with conditionsCREATEORREPLACEFUNCTIONbefore_user_update()RETURNSTRIGGERAS$$BEGINNEW.updated_at=NOW();-- Check if email changedIFNEW.email!=OLD.emailTHENNEW.email_verified=FALSE;ENDIF;-- Prevent username changesIFNEW.username!=OLD.usernameTHENRAISEEXCEPTION'Username cannot be changed';ENDIF;RETURNNEW;END;$$LANGUAGEplpgsql;CREATETRIGGERbefore_user_update_triggerBEFOREUPDATEONusersFOREACHROWEXECUTEFUNCTIONbefore_user_update();-- AFTER UPDATE triggerCREATEORREPLACEFUNCTIONafter_user_update()RETURNSTRIGGERAS$$BEGININSERTINTOuser_audit(user_id,action,old_values,new_values,timestamp)VALUES(NEW.id,'UPDATE',row_to_json(OLD),row_to_json(NEW),NOW());RETURNNEW;END;$$LANGUAGEplpgsql;CREATETRIGGERafter_user_update_triggerAFTERUPDATEONusersFOREACHROWEXECUTEFUNCTIONafter_user_update();-- BEFORE DELETE triggerCREATEORREPLACEFUNCTIONbefore_user_delete()RETURNSTRIGGERAS$$BEGIN-- Archive user before deletionINSERTINTOdeleted_users(original_id,username,email,deleted_at)VALUES(OLD.id,OLD.username,OLD.email,NOW());RETURNOLD;END;$$LANGUAGEplpgsql;CREATETRIGGERbefore_user_delete_triggerBEFOREDELETEONusersFOREACHROWEXECUTEFUNCTIONbefore_user_delete();
-- List triggers\dSSELECTtrigger_name,event_manipulation,event_object_tableFROMinformation_schema.triggersWHEREtrigger_schema='public';-- Show trigger definition\d+table_name-- Disable triggerALTERTABLEusersDISABLETRIGGERupdate_users_updated_at;-- Enable triggerALTERTABLEusersENABLETRIGGERupdate_users_updated_at;-- Disable all triggers on tableALTERTABLEusersDISABLETRIGGERALL;-- Enable all triggers on tableALTERTABLEusersENABLETRIGGERALL;-- Drop triggerDROPTRIGGERIFEXISTSupdate_users_updated_atONusers;-- Drop trigger functionDROPFUNCTIONIFEXISTSupdate_modified_column();```_##ViewsandMaterializedViews##Creandovistas```sql-- Simple viewCREATEVIEWactive_usersASSELECTid,username,email,created_atFROMusersWHEREis_active=TRUE;-- View with JOINCREATEVIEWuser_postsASSELECTu.username,u.email,p.title,p.content,p.created_ataspost_dateFROMusersuJOINpostspONu.id=p.user_id;-- View with aggregationCREATEVIEWuser_statsASSELECTu.id,u.username,COUNT(p.id)aspost_count,MAX(p.created_at)aslast_post_date,AVG(LENGTH(p.content))asavg_post_lengthFROMusersuLEFTJOINpostspONu.id=p.user_idGROUPBYu.id,u.username;-- View with window functionsCREATEVIEWuser_rankingsASSELECTusername,post_count,RANK()OVER(ORDERBYpost_countDESC)asrank,PERCENT_RANK()OVER(ORDERBYpost_countDESC)aspercentileFROM(SELECTu.username,COUNT(p.id)aspost_countFROMusersuLEFTJOINpostspONu.id=p.user_idGROUPBYu.username)user_post_counts;-- Recursive viewCREATERECURSIVEVIEWemployee_hierarchyASSELECTid,name,manager_id,1aslevelFROMemployeesWHEREmanager_idISNULLUNIONALLSELECTe.id,e.name,e.manager_id,eh.level+1FROMemployeeseJOINemployee_hierarchyehONe.manager_id=eh.id;
-- Create materialized viewCREATEMATERIALIZEDVIEWuser_summaryASSELECTDATE_TRUNC('month',created_at)asmonth,COUNT(*)asuser_count,COUNT(*)FILTER(WHEREis_active=TRUE)asactive_countFROMusersGROUPBYDATE_TRUNC('month',created_at)ORDERBYmonth;-- Create materialized view with indexCREATEMATERIALIZEDVIEWproduct_sales_summaryASSELECTp.id,p.name,SUM(oi.quantity)astotal_sold,SUM(oi.quantity*p.price)astotal_revenueFROMproductspJOINorder_itemsoiONp.id=oi.product_idGROUPBYp.id,p.name;CREATEINDEXidx_product_sales_summary_revenueONproduct_sales_summary(total_revenueDESC);-- Materialized view with dataCREATEMATERIALIZEDVIEWmonthly_salesASSELECTDATE_TRUNC('month',order_date)asmonth,SUM(total_amount)astotal_sales,COUNT(*)asorder_count,AVG(total_amount)asavg_order_valueFROMordersWHEREorder_date>='2023-01-01'GROUPBYDATE_TRUNC('month',order_date)WITHDATA;-- Materialized view without data (populate later)CREATEMATERIALIZEDVIEWlarge_summaryASSELECT*FROMvery_large_tableWITHNODATA;
-- Create updatable viewCREATEVIEWuser_profilesASSELECTid,username,email,first_name,last_nameFROMusersWHEREis_active=TRUE;-- Insert through viewINSERTINTOuser_profiles(username,email,first_name,last_name)VALUES('newuser','new@example.com','New','User');-- Update through viewUPDATEuser_profilesSETemail='updated@example.com'WHEREid=1;-- Delete through viewDELETEFROMuser_profilesWHEREid=1;-- View with INSTEAD OF triggers for complex updatesCREATEORREPLACEFUNCTIONupdate_user_profile()RETURNSTRIGGERAS$$BEGINIFTG_OP='INSERT'THENINSERTINTOusers(username,email,first_name,last_name,is_active)VALUES(NEW.username,NEW.email,NEW.first_name,NEW.last_name,TRUE);RETURNNEW;ELSIFTG_OP='UPDATE'THENUPDATEusersSETusername=NEW.username,email=NEW.email,first_name=NEW.first_name,last_name=NEW.last_nameWHEREid=NEW.id;RETURNNEW;ELSIFTG_OP='DELETE'THENUPDATEusersSETis_active=FALSEWHEREid=OLD.id;RETURNOLD;ENDIF;RETURNNULL;END;$$LANGUAGEplpgsql;CREATETRIGGERuser_profiles_triggerINSTEADOFINSERTORUPDATEORDELETEONuser_profilesFOREACHROWEXECUTEFUNCTIONupdate_user_profile();
-- Create userCREATEUSERusernameWITHPASSWORD'password';-- Create user with optionsCREATEUSERusernameWITHPASSWORD'password'CREATEDBCREATEROLELOGINVALIDUNTIL'2024-12-31';-- Create role (cannot login by default)CREATEROLErole_name;-- Create role with login capabilityCREATEROLErole_nameWITHLOGINPASSWORD'password';-- Create role with specific attributesCREATEROLEapp_roleWITHNOLOGINCREATEDBCREATEROLECONNECTIONLIMIT10;
-- Grant database privilegesGRANTCONNECTONDATABASEmydatabaseTOusername;GRANTCREATEONDATABASEmydatabaseTOusername;GRANTALLPRIVILEGESONDATABASEmydatabaseTOusername;-- Grant schema privilegesGRANTUSAGEONSCHEMApublicTOusername;GRANTCREATEONSCHEMApublicTOusername;GRANTALLONSCHEMApublicTOusername;-- Grant table privilegesGRANTSELECTONusersTOusername;GRANTINSERT,UPDATE,DELETEONusersTOusername;GRANTALLPRIVILEGESONusersTOusername;-- Grant privileges on all tables in schemaGRANTSELECTONALLTABLESINSCHEMApublicTOusername;GRANTALLPRIVILEGESONALLTABLESINSCHEMApublicTOusername;-- Grant privileges on future tablesALTERDEFAULTPRIVILEGESINSCHEMApublicGRANTSELECTONTABLESTOusername;-- Grant sequence privilegesGRANTUSAGE,SELECTONSEQUENCEusers_id_seqTOusername;GRANTALLONALLSEQUENCESINSCHEMApublicTOusername;-- Grant function privilegesGRANTEXECUTEONFUNCTIONfunction_nameTOusername;GRANTEXECUTEONALLFUNCTIONSINSCHEMApublicTOusername;-- Grant column-level privilegesGRANTSELECT(username,email),UPDATE(email)ONusersTOusername;
-- Grant role to userGRANTrole_nameTOusername;-- Grant role with admin optionGRANTrole_nameTOusernameWITHADMINOPTION;-- Revoke role from userREVOKErole_nameFROMusername;-- Set default roleALTERUSERusernameSETROLErole_name;-- Create role hierarchyCREATEROLEread_only;CREATEROLEread_write;CREATEROLEadmin;GRANTread_onlyTOread_write;GRANTread_writeTOadmin;-- Grant privileges to rolesGRANTSELECTONALLTABLESINSCHEMApublicTOread_only;GRANTINSERT,UPDATE,DELETEONALLTABLESINSCHEMApublicTOread_write;GRANTALLPRIVILEGESONALLTABLESINSCHEMApublicTOadmin;
-- Show current userSELECTcurrent_user;-- Show current roleSELECTcurrent_role;-- Show user privileges\duSELECT*FROMpg_user;SELECT*FROMpg_roles;-- Show table privileges\dptable_nameSELECTgrantee,privilege_typeFROMinformation_schema.role_table_grantsWHEREtable_name='users';-- Show database privilegesSELECTdatname,dataclFROMpg_databaseWHEREdatname='mydatabase';-- Revoke privilegesREVOKESELECTONusersFROMusername;REVOKEALLPRIVILEGESONusersFROMusername;REVOKECONNECTONDATABASEmydatabaseFROMusername;-- Change user passwordALTERUSERusernameWITHPASSWORD'new_password';-- Change user attributesALTERUSERusernameWITHCREATEDB;ALTERUSERusernameWITHNOCREATEDB;ALTERUSERusernameWITHCREATEROLE;ALTERUSERusernameWITHNOCREATEROLE;-- Set connection limitALTERUSERusernameWITHCONNECTIONLIMIT5;-- Set password expirationALTERUSERusernameWITHVALIDUNTIL'2024-12-31';-- Lock user accountALTERUSERusernameWITHNOLOGIN;-- Unlock user accountALTERUSERusernameWITHLOGIN;-- Drop userDROPUSERusername;-- Drop roleDROPROLErole_name;
-- Enable RLS on tableALTERTABLEusersENABLEROWLEVELSECURITY;-- Create policyCREATEPOLICYuser_policyONusersFORALLTOapplication_roleUSING(user_id=current_setting('app.current_user_id')::INTEGER);-- Create policy for SELECTCREATEPOLICYselect_own_postsONpostsFORSELECTTOapplication_roleUSING(user_id=current_setting('app.current_user_id')::INTEGER);-- Create policy for INSERTCREATEPOLICYinsert_own_postsONpostsFORINSERTTOapplication_roleWITHCHECK(user_id=current_setting('app.current_user_id')::INTEGER);-- Create policy for UPDATECREATEPOLICYupdate_own_postsONpostsFORUPDATETOapplication_roleUSING(user_id=current_setting('app.current_user_id')::INTEGER)WITHCHECK(user_id=current_setting('app.current_user_id')::INTEGER);-- Create policy for DELETECREATEPOLICYdelete_own_postsONpostsFORDELETETOapplication_roleUSING(user_id=current_setting('app.current_user_id')::INTEGER);-- Show policies\d+table_nameSELECT*FROMpg_policiesWHEREtablename='users';-- Disable RLSALTERTABLEusersDISABLEROWLEVELSECURITY;-- Drop policyDROPPOLICYuser_policyONusers;-- Force RLS for table ownersALTERTABLEusersFORCEROWLEVELSECURITY;
# Backup single databasepg_dump-Uusername-hhostnamedatabase_name>backup.sql
# Backup with specific optionspg_dump-Uusername-hhostname\--verbose\--clean\--no-owner\--no-privileges\database_name>backup.sql
# Backup in custom format (compressed)pg_dump-Uusername-hhostname-Fcdatabase_name>backup.dump
# Backup in directory formatpg_dump-Uusername-hhostname-Fddatabase_name-fbackup_dir
# Backup specific tablespg_dump-Uusername-hhostname-tusers-tpostsdatabase_name>tables_backup.sql
# Backup specific schemapg_dump-Uusername-hhostname-npublicdatabase_name>schema_backup.sql
# Backup structure only (no data)pg_dump-Uusername-hhostname--schema-onlydatabase_name>structure.sql
# Backup data only (no structure)pg_dump-Uusername-hhostname--data-onlydatabase_name>data.sql
# Backup with parallel jobspg_dump-Uusername-hhostname-Fd-j4database_name-fbackup_dir
# Exclude specific tablespg_dump-Uusername-hhostname--exclude-table=logsdatabase_name>backup.sql
# Backup all databasespg_dumpall-Uusername-hhostname>all_databases.sql
# Backup only global objects (roles, tablespaces)pg_dumpall-Uusername-hhostname--globals-only>globals.sql
# Restore from SQL filepsql-Uusername-hhostname-ddatabase_name<backup.sql
# Restore and create databasecreatedb-Uusername-hhostnamedatabase_name
psql-Uusername-hhostname-ddatabase_name<backup.sql
# Restore from custom formatpg_restore-Uusername-hhostname-ddatabase_namebackup.dump
# Restore with specific optionspg_restore-Uusername-hhostname\--verbose\--clean\--no-owner\--no-privileges\-ddatabase_namebackup.dump
# Restore specific tablespg_restore-Uusername-hhostname-ddatabase_name-tusersbackup.dump
# Restore with parallel jobspg_restore-Uusername-hhostname-ddatabase_name-j4backup.dump
# List contents of backup filepg_restore--listbackup.dump
# Restore from directory formatpg_restore-Uusername-hhostname-ddatabase_namebackup_dir
# Restore all databasespsql-Uusername-hhostname<all_databases.sql
-- Use EXPLAIN to analyze queriesEXPLAINSELECT*FROMusersWHEREusername='john_doe';-- Use EXPLAIN ANALYZE for actual execution statsEXPLAIN(ANALYZE,BUFFERS)SELECT*FROMusersWHEREusername='john_doe';-- Use EXPLAIN with different optionsEXPLAIN(ANALYZE,BUFFERS,VERBOSE,FORMATJSON)SELECTu.username,COUNT(p.id)aspost_countFROMusersuLEFTJOINpostspONu.id=p.user_idGROUPBYu.username;-- Optimize WHERE clauses-- Bad: Function on column prevents index usageSELECT*FROMusersWHEREUPPER(username)='JOHN_DOE';-- Good: Use functional index or rewrite queryCREATEINDEXidx_users_upper_usernameONusers(UPPER(username));-- OrSELECT*FROMusersWHEREusername='john_doe';-- Use appropriate JOIN types-- Use EXISTS instead of IN for better performanceSELECT*FROMusersuWHEREEXISTS(SELECT1FROMpostspWHEREp.user_id=u.id);-- Use LIMIT for large result setsSELECT*FROMusersORDERBYcreated_atDESCLIMIT10;-- Use partial indexes for filtered queriesCREATEINDEXidx_active_usersONusers(username)WHEREis_active=TRUE;
Optimización del índice
-- Analyze table statisticsANALYZEusers;-- Update all table statisticsANALYZE;-- Check index usageSELECTschemaname,tablename,indexname,idx_tup_read,idx_tup_fetch,idx_scanFROMpg_stat_user_indexesORDERBYidx_scanDESC;-- Find unused indexesSELECTschemaname,tablename,indexname,idx_scan,pg_size_pretty(pg_relation_size(indexrelid))assizeFROMpg_stat_user_indexesWHEREidx_scan=0ORDERBYpg_relation_size(indexrelid)DESC;-- Check index bloatSELECTschemaname,tablename,indexname,pg_size_pretty(pg_relation_size(indexrelid))assize,CASEWHENpg_relation_size(indexrelid)=0THEN0ELSE(pgstatindex(indexrelid)).avg_leaf_densityENDasavg_leaf_densityFROMpg_stat_user_indexes;-- Reindex to reduce bloatREINDEXINDEXidx_users_username;REINDEXTABLEusers;REINDEXDATABASEmydatabase;-- Create covering indexesCREATEINDEXidx_users_coveringONusers(username)INCLUDE(email,created_at);
-- Show current configurationSHOWALL;-- Important parameters to tuneSHOWshared_buffers;-- Should be 25% of RAMSHOWeffective_cache_size;-- Should be 50-75% of RAMSHOWwork_mem;-- Per-operation memorySHOWmaintenance_work_mem;-- For maintenance operationsSHOWmax_connections;-- Based on application needsSHOWcheckpoint_segments;-- For write-heavy workloadsSHOWwal_buffers;-- WAL buffer size-- Set parameters for sessionSETwork_mem='256MB';SETenable_seqscan=off;-- Show query execution statisticsSELECTquery,calls,total_time,mean_time,rowsFROMpg_stat_statementsORDERBYtotal_timeDESCLIMIT10;-- Reset statisticsSELECTpg_stat_reset();SELECTpg_stat_statements_reset();
-- On master: Check replication statusSELECTclient_addr,state,sent_lsn,write_lsn,flush_lsn,replay_lsn,sync_stateFROMpg_stat_replication;-- On slave: Check replication lagSELECTnow()-pg_last_xact_replay_timestamp()ASreplication_lag;-- Check if in recovery modeSELECTpg_is_in_recovery();-- Show recovery statusSELECTpg_last_wal_receive_lsn(),pg_last_wal_replay_lsn(),pg_last_xact_replay_timestamp();
-- On publisher: Create publicationCREATEPUBLICATIONmy_publicationFORALLTABLES;-- Or for specific tablesCREATEPUBLICATIONmy_publicationFORTABLEusers,posts;-- On subscriber: Create subscriptionCREATESUBSCRIPTIONmy_subscriptionCONNECTION'host=publisher_ip port=5432 user=replication_user password=password dbname=mydatabase'PUBLICATIONmy_publication;-- Monitor logical replicationSELECT*FROMpg_publication;SELECT*FROMpg_subscription;SELECT*FROMpg_stat_subscription;-- Add table to publicationALTERPUBLICATIONmy_publicationADDTABLEnew_table;-- Remove table from publicationALTERPUBLICATIONmy_publicationDROPTABLEold_table;-- Refresh subscriptionALTERSUBSCRIPTIONmy_subscriptionREFRESHPUBLICATION;-- Drop subscriptionDROPSUBSCRIPTIONmy_subscription;-- Drop publicationDROPPUBLICATIONmy_publication;
# Promote slave to masterpg_ctlpromote-D/var/lib/postgresql/data
# Or create trigger filetouch/tmp/postgresql.trigger
# Planned switchover# 1. Stop writes to master# 2. Wait for slave to catch up# 3. Promote slave# 4. Reconfigure applications# 5. Set up old master as new slave
-- List available extensionsSELECT*FROMpg_available_extensionsORDERBYname;-- List installed extensions\dxSELECT*FROMpg_extension;-- Install extensionCREATEEXTENSIONIFNOTEXISTSextension_name;-- Install extension in specific schemaCREATEEXTENSIONextension_nameSCHEMAschema_name;-- Update extensionALTEREXTENSIONextension_nameUPDATE;-- Drop extensionDROPEXTENSIONextension_name;
-- GIN index on JSONB columnCREATEINDEXidx_metadata_ginONjson_testUSINGGIN(metadata);-- GIN index on specific JSON pathCREATEINDEXidx_metadata_preferencesONjson_testUSINGGIN((metadata->'preferences'));-- B-tree index on extracted valueCREATEINDEXidx_metadata_themeONjson_test((metadata->'preferences'->>'theme'));-- Partial indexCREATEINDEXidx_premium_usersONjson_testUSINGGIN(metadata)WHEREmetadata->'tags'?'premium';-- Expression indexCREATEINDEXidx_metadata_keysONjson_testUSINGGIN(jsonb_object_keys(metadata));
-- Show available text search configurationsSELECTcfgnameFROMpg_ts_config;-- Show available dictionariesSELECTdictnameFROMpg_ts_dict;-- Show available parsersSELECTprsnameFROMpg_ts_parser;-- Create custom text search configurationCREATETEXTSEARCHCONFIGURATIONmy_config(COPY=english);-- Modify configurationALTERTEXTSEARCHCONFIGURATIONmy_configALTERMAPPINGFORwordWITHenglish_stem,simple;-- Set default configurationSETdefault_text_search_config='my_config';```_##AdvancedTextSearch```sql-- Add tsvector column for better performanceALTERTABLEdocumentsADDCOLUMNsearch_vectorTSVECTOR;-- Update search vectorUPDATEdocumentsSETsearch_vector=to_tsvector('english',coalesce(title,'')||' '||coalesce(content,''));-- Create GIN indexCREATEINDEXidx_documents_searchONdocumentsUSINGGIN(search_vector);-- Create trigger to maintain search vectorCREATEORREPLACEFUNCTIONupdate_search_vector()RETURNSTRIGGERAS$$BEGINNEW.search_vector:=to_tsvector('english',coalesce(NEW.title,'')||' '||coalesce(NEW.content,''));RETURNNEW;END;$$LANGUAGEplpgsql;CREATETRIGGERupdate_documents_search_vectorBEFOREINSERTORUPDATEONdocumentsFOREACHROWEXECUTEFUNCTIONupdate_search_vector();-- Complex queriesSELECT*FROMdocumentsWHEREsearch_vector@@to_tsquery('english','PostgreSQL & database');SELECT*FROMdocumentsWHEREsearch_vector@@to_tsquery('english','PostgreSQL | MySQL');SELECT*FROMdocumentsWHEREsearch_vector@@to_tsquery('english','database & !Oracle');-- Phrase searchSELECT*FROMdocumentsWHEREsearch_vector@@phraseto_tsquery('english','database management system');-- Fuzzy search with similarityCREATEEXTENSIONIFNOTEXISTSpg_trgm;SELECT*FROMdocumentsWHEREsimilarity(title,'PostgreSQL')>0.3ORDERBYsimilarity(title,'PostgreSQL')DESC;
-- Highlighting search resultsSELECTtitle,ts_headline('english',content,to_tsquery('english','database'),'StartSel=<b>, StopSel=</b>, MaxWords=35, MinWords=15')assnippetFROMdocumentsWHEREsearch_vector@@to_tsquery('english','database');-- Get search statisticsSELECTword,ndoc,nentryFROMts_stat('SELECT search_vector FROM documents');-- Parse text into tokensSELECT*FROMts_parse('default','PostgreSQL is a powerful database system');-- Convert text to tsvector with positionsSELECTto_tsvector('english','PostgreSQL is a powerful database system');-- Show lexemesSELECTunnest(tsvector_to_array(to_tsvector('english','PostgreSQL databases')));
-- Current activitySELECTpid,usename,application_name,client_addr,state,query_start,now()-query_startasduration,queryFROMpg_stat_activityWHEREstate!='idle'ORDERBYquery_start;-- Database statisticsSELECTdatname,numbackends,xact_commit,xact_rollback,blks_read,blks_hit,tup_returned,tup_fetched,tup_inserted,tup_updated,tup_deletedFROMpg_stat_database;-- Table statisticsSELECTschemaname,tablename,seq_scan,seq_tup_read,idx_scan,idx_tup_fetch,n_tup_ins,n_tup_upd,n_tup_del,n_live_tup,n_dead_tupFROMpg_stat_user_tablesORDERBYseq_scanDESC;-- Index statisticsSELECTschemaname,tablename,indexname,idx_scan,idx_tup_read,idx_tup_fetchFROMpg_stat_user_indexesORDERBYidx_scanDESC;
-- Use appropriate data types-- Bad: Using TEXT for everythingCREATETABLEbad_design(idTEXT,ageTEXT,priceTEXT,is_activeTEXT);-- Good: Specific data typesCREATETABLEgood_design(idSERIALPRIMARYKEY,ageSMALLINTCHECK(age>=0ANDage<=150),priceDECIMAL(10,2)CHECK(price>=0),is_activeBOOLEANDEFAULTTRUE);-- Normalize your database-- Avoid redundant data-- Use foreign keys to maintain referential integrity-- Use meaningful names-- BadCREATETABLEu(iINTEGER,nVARCHAR(50),eVARCHAR(100));-- GoodCREATETABLEusers(idSERIALPRIMARYKEY,usernameVARCHAR(50)NOTNULLUNIQUE,emailVARCHAR(100)NOTNULLUNIQUE);-- Add appropriate constraintsALTERTABLEpostsADDCONSTRAINTfk_posts_user_idFOREIGNKEY(user_id)REFERENCESusers(id)ONDELETECASCADE;ALTERTABLEproductsADDCONSTRAINTchk_products_priceCHECK(price>0);-- Use partial indexes for filtered queriesCREATEINDEXidx_active_usersONusers(username)WHEREis_active=TRUE;-- Use covering indexes to avoid table lookupsCREATEINDEXidx_users_coveringONusers(username)INCLUDE(email,created_at);
-- Use prepared statements to prevent SQL injectionPREPAREget_userASSELECT*FROMusersWHEREid=$1;EXECUTEget_user(1);DEALLOCATEget_user;-- Use specific columns instead of SELECT *-- BadSELECT*FROMusers;-- GoodSELECTid,username,emailFROMusers;-- Use EXISTS instead of IN for better performance-- BadSELECT*FROMusersWHEREidIN(SELECTuser_idFROMposts);-- GoodSELECT*FROMusersuWHEREEXISTS(SELECT1FROMpostspWHEREp.user_id=u.id);-- Use LIMIT for large result setsSELECT*FROMusersORDERBYcreated_atDESCLIMIT10;-- Use proper JOIN syntaxSELECTu.username,p.titleFROMusersuINNERJOINpostspONu.id=p.user_idWHEREu.is_active=TRUE;-- Use window functions instead of subqueries when possible-- BadSELECTusername,(SELECTCOUNT(*)FROMpostsWHEREuser_id=users.id)aspost_countFROMusers;-- GoodSELECTu.username,COUNT(p.id)aspost_countFROMusersuLEFTJOINpostspONu.id=p.user_idGROUPBYu.username;-- Use CTEs for complex queriesWITHrecent_usersAS(SELECT*FROMusersWHEREcreated_at>'2023-01-01'),user_statsAS(SELECTu.id,u.username,COUNT(p.id)aspost_countFROMrecent_usersuLEFTJOINpostspONu.id=p.user_idGROUPBYu.id,u.username)SELECT*FROMuser_statsWHEREpost_count>5;
-- Regular maintenanceANALYZE;VACUUM;REINDEXDATABASEmydatabase;-- Monitor query performanceEXPLAIN(ANALYZE,BUFFERS)SELECT*FROMusersWHEREusername='john_doe';-- Use connection pooling in applications-- Configure appropriate connection limits-- Partition large tablesCREATETABLEsales(idSERIAL,sale_dateDATE,amountDECIMAL(10,2))PARTITIONBYRANGE(sale_date);CREATETABLEsales_2023PARTITIONOFsalesFORVALUESFROM('2023-01-01')TO('2024-01-01');-- Use read replicas for read-heavy workloads-- Separate read and write operations-- Configure autovacuum appropriatelyALTERTABLElarge_tableSET(autovacuum_vacuum_scale_factor=0.1,autovacuum_analyze_scale_factor=0.05);-- Use materialized views for expensive queriesCREATEMATERIALIZEDVIEWuser_summaryASSELECTDATE_TRUNC('month',created_at)asmonth,COUNT(*)asuser_countFROMusersGROUPBYDATE_TRUNC('month',created_at);-- Refresh materialized views regularlyREFRESHMATERIALIZEDVIEWuser_summary;
-- Use least privilege principle-- Grant only necessary permissionsGRANTSELECT,INSERT,UPDATEONusersTOapp_user;-- Use row level securityALTERTABLEusersENABLEROWLEVELSECURITY;CREATEPOLICYuser_policyONusersFORALLTOapp_roleUSING(user_id=current_setting('app.current_user_id')::INTEGER);-- Use SSL connections-- Configure pg_hba.conf to require SSL-- hostssl all all 0.0.0.0/0 md5-- Encrypt sensitive dataCREATEEXTENSIONIFNOTEXISTSpgcrypto;-- Hash passwordsINSERTINTOusers(username,password_hash)VALUES('john',crypt('password',gen_salt('bf')));-- Validate passwordsSELECT*FROMusersWHEREusername='john'ANDpassword_hash=crypt('password',password_hash);-- Use audit loggingCREATETABLEaudit_log(idSERIALPRIMARYKEY,table_nameVARCHAR(50),operationVARCHAR(10),user_nameVARCHAR(50),timestampTIMESTAMPDEFAULTNOW(),old_valuesJSONB,new_valuesJSONB);-- Regular security updates-- Monitor for suspicious activity-- Backup encryption keys securely
PostgreSQL es un avanzado sistema de bases de datos relacionales que ofrece un excelente rendimiento, fiabilidad y extensibilidad. Esta hoja de trampa completa cubre las operaciones esenciales de PostgreSQL desde la instalación básica a temas avanzados como replicación, búsqueda de texto completo y optimización de rendimiento.
Key Strengths
- Cumplimiento de la CIA: Soporte completo de transacción con fuerte consistencia
- Extensibilidad: Ecosistema de extensión rico y tipos de datos personalizados
- Características avanzadas: JSON/JSONB, búsqueda de texto completo, funciones de ventana, CTEs
- Performance: Planificador de consultas sofisticado y características de optimización
- ** Cumplimiento de los clientes**: Excelente cumplimiento estándar de SQL
Mejores casos de uso:
- Aplicaciones web complejas que requieren características SQL avanzadas
- Aplicaciones de almacenamiento de datos y análisis
- Aplicaciones geoespaciales (con extensión PostGIS)
- Aplicaciones que requieren almacenamiento de documentos JSON
- Aplicaciones empresariales con lógica empresarial compleja
** Consideraciones importantes:**
- El mantenimiento regular (VACUUM, ANALYZE) es crucial para un rendimiento óptimo
- Una estrategia adecuada de indexación es esencial para el desempeño de las consultas
- El ajuste de configuración debe basarse en las características del volumen de trabajo
- Se debe vigilar y alertar a los sistemas de producción
Siguiendo las prácticas y técnicas descritas en esta hoja de trampa, puede diseñar, implementar y mantener eficazmente bases de datos PostgreSQL seguras, performantes y fiables para cualquier escala de aplicación.