Adminer Cheatsheet¶
Adminer - Database Management Tool
Adminer (früher phpMinAdmin) ist ein umfassendes Datenbankverwaltungstool, das in PHP geschrieben wurde. Es besteht aus einer einzelnen PHP-Datei, die auf dem Zielserver bereitgestellt werden kann. Adminer ist für MySQL, MariaDB, PostgreSQL, SQLite, MS SQL, Oracle, Elasticsearch, MongoDB und mehr verfügbar.
Inhaltsverzeichnis¶
- Installation
- Konfiguration
- Datenbankverbindungen
- Datenbankoperationen
- Tabellenverwaltung
- Datenoperationen
- SQL-Editor
- Import/Export
- Benutzerverwaltung
- Sicherheit
- Anpassung
- Best Practices
Installation¶
Grundlegende Installation¶
# Download latest version
wget https://www.adminer.org/latest.php -O adminer.php
# Or download specific version
wget https://github.com/vrana/adminer/releases/download/v4.8.1/adminer-4.8.1.php -O adminer.php
# Make it accessible via web server
sudo cp adminer.php /var/www/html/
sudo chown www-data:www-data /var/www/html/adminer.php
sudo chmod 644 /var/www/html/adminer.php
# Access via browser
# http://your-server.com/adminer.php
Docker-Installation¶
# Run Adminer in Docker
docker run --name adminer -p 8080:8080 adminer
# With specific tag
docker run --name adminer -p 8080:8080 adminer:4.8.1
# With custom configuration
docker run --name adminer \
-p 8080:8080 \
-e ADMINER_DEFAULT_SERVER=mysql \
-e ADMINER_DESIGN=pepa-linha \
adminer
# Access at http://localhost:8080
# Docker Compose setup
version: '3.8'
services:
adminer:
image: adminer:4.8.1
restart: always
ports:
- 8080:8080
environment:
ADMINER_DEFAULT_SERVER: mysql
ADMINER_DESIGN: pepa-linha
volumes:
- ./adminer-plugins:/var/www/html/plugins-enabled
Webserver-Konfiguration¶
# Apache configuration
<VirtualHost *:80>
ServerName adminer.example.com
DocumentRoot /var/www/adminer
<Directory /var/www/adminer>
Options -Indexes
AllowOverride All
Require all granted
# Security headers
Header always set X-Content-Type-Options nosniff
Header always set X-Frame-Options DENY
Header always set X-XSS-Protection "1; mode=block"
</Directory>
# Restrict access to specific IPs
<Location />
Require ip 192.168.1.0/24
Require ip 10.0.0.0/8
</Location>
</VirtualHost>
# Nginx configuration
server {
listen 80;
server_name adminer.example.com;
root /var/www/adminer;
index adminer.php;
# Security headers
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options DENY;
add_header X-XSS-Protection "1; mode=block";
# Restrict access
allow 192.168.1.0/24;
allow 10.0.0.0/8;
deny all;
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# Deny access to sensitive files
location ~ /\. {
deny all;
}
}
PHP-Anforderungen¶
<?php
// Check PHP requirements
echo "PHP Version: " . PHP_VERSION . "\n";
// Required extensions
$required_extensions = [
'pdo',
'pdo_mysql', // For MySQL/MariaDB
'pdo_pgsql', // For PostgreSQL
'pdo_sqlite', // For SQLite
'pdo_sqlsrv', // For SQL Server
'pdo_oci', // For Oracle
'mongodb', // For MongoDB
'json',
'session',
'pcre'
];
foreach ($required_extensions as $ext) {
if (extension_loaded($ext)) {
echo "✓ $ext extension loaded\n";
} else {
echo "✗ $ext extension NOT loaded\n";
}
}
// Memory and time limits
echo "Memory Limit: " . ini_get('memory_limit') . "\n";
echo "Max Execution Time: " . ini_get('max_execution_time') . "\n";
echo "Upload Max Filesize: " . ini_get('upload_max_filesize') . "\n";
echo "Post Max Size: " . ini_get('post_max_size') . "\n";
?>
Konfiguration¶
Grundlegende Konfiguration¶
<?php
// adminer-config.php - Custom configuration file
// Include original Adminer
include './adminer.php';
// Custom configuration class
class AdminerConfig extends Adminer {
// Default database server
function database() {
return 'myapp_db';
}
// Default username
function username() {
return 'admin';
}
// Hide certain databases
function databases($flush = true) {
return array('myapp_db', 'analytics_db');
}
// Custom login credentials
function login($login, $password) {
// Allow specific users
$users = array(
'admin' => 'secure_password_hash',
'readonly' => 'readonly_password_hash'
);
return isset($users[$login]) &&
password_verify($password, $users[$login]);
}
// Customize interface
function name() {
return 'MyApp Database Admin';
}
// Custom CSS
function head() {
echo '<style>
body { font-family: Arial, sans-serif; }
#menu { background: #2c3e50; }
#menu a { color: white; }
</style>';
}
}
// Use custom configuration
new AdminerConfig;
?>
Umgebungsspezifische Konfiguration¶
<?php
// config/development.php
class AdminerDevelopment extends Adminer {
function login($login, $password) {
// Allow any login in development
return true;
}
function database() {
return 'myapp_dev';
}
function name() {
return 'Development Database';
}
}
// config/production.php
class AdminerProduction extends Adminer {
function login($login, $password) {
// Strict authentication in production
$allowed_users = [
'dba' => '$2y$10$hash_for_dba_password',
'readonly' => '$2y$10$hash_for_readonly_password'
];
return isset($allowed_users[$login]) &&
password_verify($password, $allowed_users[$login]);
}
function databases() {
// Only show production databases
return ['myapp_prod', 'analytics_prod'];
}
function name() {
return 'Production Database - CAUTION';
}
// Disable dangerous operations
function drop() {
return false; // Disable DROP operations
}
function alter() {
return false; // Disable ALTER operations
}
}
// Load environment-specific config
$environment = $_ENV['APP_ENV'] ?? 'development';
include "config/{$environment}.php";
?>
Plugin-System¶
<?php
// plugins-enabled/plugin.php
// Enable specific plugins
// Available plugins:
// - designs (themes)
// - dump-date (add date to exports)
// - dump-zip (compress exports)
// - edit-calendar (date picker)
// - edit-foreign (foreign key editor)
// - edit-textarea (larger text areas)
// - email-table (email table contents)
// - enum-option (enum field editor)
// - file-upload (file upload interface)
// - foreign-system (cross-database foreign keys)
// - frames (frame interface)
// - json-column (JSON column editor)
// - login-password-less (passwordless login)
// - login-servers (predefined servers)
// - login-ssl (SSL connection options)
// - master-password (master password protection)
// - readonly (read-only mode)
// - slug (SEO-friendly URLs)
// - struct-comments (structure comments)
// - table-structure (enhanced table structure)
// - tinymce (rich text editor)
// - translation (custom translations)
// - version-noverify (skip version verification)
// Enable plugins
$plugins = [
'designs',
'edit-calendar',
'edit-textarea',
'dump-zip',
'json-column'
];
foreach ($plugins as $plugin) {
if (file_exists("plugins/$plugin.php")) {
include "plugins/$plugin.php";
}
}
?>
Datenbankverbindungen¶
Verbindungsparameter¶
<?php
// Connection configuration for different databases
// MySQL/MariaDB connection
$mysql_config = [
'server' => 'localhost:3306',
'username' => 'dbuser',
'password' => 'password',
'database' => 'myapp',
'driver' => 'mysql'
];
// PostgreSQL connection
$pgsql_config = [
'server' => 'localhost:5432',
'username' => 'postgres',
'password' => 'password',
'database' => 'myapp',
'driver' => 'pgsql'
];
// SQLite connection
$sqlite_config = [
'server' => '',
'username' => '',
'password' => '',
'database' => '/path/to/database.sqlite',
'driver' => 'sqlite'
];
// SQL Server connection
$sqlsrv_config = [
'server' => 'localhost:1433',
'username' => 'sa',
'password' => 'password',
'database' => 'myapp',
'driver' => 'mssql'
];
// Oracle connection
$oracle_config = [
'server' => 'localhost:1521/XE',
'username' => 'hr',
'password' => 'password',
'database' => '',
'driver' => 'oracle'
];
?>
SSL/TLS-Konfiguration¶
<?php
// SSL connection configuration
class AdminerSSL extends Adminer {
function connectSsl() {
return [
MYSQLI_CLIENT_SSL => true,
MYSQLI_CLIENT_SSL_VERIFY_SERVER_CERT => true
];
}
function credentials() {
return [
'server' => 'secure-db.example.com:3306',
'username' => 'ssl_user',
'password' => 'ssl_password',
'ssl' => [
'key' => '/path/to/client-key.pem',
'cert' => '/path/to/client-cert.pem',
'ca' => '/path/to/ca-cert.pem',
'capath' => '/path/to/ca-certs/',
'cipher' => 'DHE-RSA-AES256-SHA'
]
];
}
}
// PostgreSQL SSL configuration
$pgsql_ssl_dsn = "pgsql:host=secure-db.example.com;port=5432;dbname=myapp;sslmode=require;sslcert=/path/to/client.crt;sslkey=/path/to/client.key;sslrootcert=/path/to/ca.crt";
?>
Verbindungs-Pooling¶
<?php
// Connection management
class AdminerConnectionPool extends Adminer {
private static $connections = [];
function connect() {
$server = $this->server();
$username = $this->username();
$password = $this->password();
$key = md5($server . $username . $password);
if (!isset(self::$connections[$key])) {
self::$connections[$key] = new PDO(
$this->dsn(),
$username,
$password,
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_PERSISTENT => true,
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4"
]
);
}
return self::$connections[$key];
}
function dsn() {
$server = $this->server();
$database = $this->database();
switch ($this->driver()) {
case 'mysql':
return "mysql:host=$server;dbname=$database;charset=utf8mb4";
case 'pgsql':
return "pgsql:host=$server;dbname=$database";
case 'sqlite':
return "sqlite:$database";
default:
return parent::dsn();
}
}
}
?>
Datenbankoperationen¶
Datenbankmanagement¶
-- Create database
CREATE DATABASE myapp_new
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
-- PostgreSQL
CREATE DATABASE myapp_new
WITH ENCODING 'UTF8'
LC_COLLATE='en_US.UTF-8'
LC_CTYPE='en_US.UTF-8';
-- Drop database
DROP DATABASE myapp_old;
-- Show databases
SHOW DATABASES;
-- PostgreSQL
SELECT datname FROM pg_database;
-- Database size information
SELECT
table_schema as database_name,
ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) as size_mb
FROM information_schema.tables
GROUP BY table_schema;
-- PostgreSQL database size
SELECT
datname as database_name,
pg_size_pretty(pg_database_size(datname)) as size
FROM pg_database
WHERE datistemplate = false;
Schema-Operationen¶
-- Create schema (PostgreSQL)
CREATE SCHEMA analytics;
CREATE SCHEMA reporting;
-- Set search path
SET search_path TO analytics, public;
-- Grant schema permissions
GRANT USAGE ON SCHEMA analytics TO app_user;
GRANT CREATE ON SCHEMA analytics TO app_user;
-- List schemas
SELECT schema_name
FROM information_schema.schemata
WHERE schema_name NOT IN ('information_schema', 'pg_catalog');
-- Schema size information
SELECT
schemaname,
COUNT(*) as table_count,
SUM(pg_total_relation_size(schemaname||'.'||tablename)) as total_size
FROM pg_tables
WHERE schemaname NOT IN ('information_schema', 'pg_catalog')
GROUP BY schemaname;
Backup und Wiederherstellung¶
# MySQL backup via Adminer
# Use Export functionality in web interface
# Or command line:
mysqldump -u username -p --single-transaction --routines --triggers myapp > backup.sql
# PostgreSQL backup
pg_dump -U username -h localhost -d myapp -f backup.sql
# SQLite backup
sqlite3 myapp.db ".backup backup.db"
# Restore MySQL
mysql -u username -p myapp < backup.sql
# Restore PostgreSQL
psql -U username -h localhost -d myapp -f backup.sql
# Restore SQLite
sqlite3 myapp.db ".restore backup.db"
Tabellenverwaltung¶
Tabellenerstellung¶
-- Create table with various data types
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) NOT NULL,
password_hash VARCHAR(255) NOT NULL,
first_name VARCHAR(50),
last_name VARCHAR(50),
date_of_birth DATE,
is_active BOOLEAN DEFAULT TRUE,
profile_data JSON,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_email (email),
INDEX idx_name (first_name, last_name),
INDEX idx_created_at (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- PostgreSQL table with advanced features
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES users(id),
order_number VARCHAR(20) UNIQUE NOT NULL,
status order_status_enum DEFAULT 'pending',
total_amount DECIMAL(10,2) NOT NULL CHECK (total_amount >= 0),
order_data JSONB,
shipping_address TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
CONSTRAINT chk_order_number CHECK (order_number ~ '^ORD-[0-9]{6}$')
);
-- Create indexes
CREATE INDEX idx_orders_user_id ON orders(user_id);
CREATE INDEX idx_orders_status ON orders(status);
CREATE INDEX idx_orders_created_at ON orders(created_at);
CREATE INDEX idx_orders_data_gin ON orders USING GIN (order_data);
-- Create enum type (PostgreSQL)
CREATE TYPE order_status_enum AS ENUM ('pending', 'processing', 'shipped', 'delivered', 'cancelled');
Tabellenmodifikation¶
-- Add column
ALTER TABLE users ADD COLUMN phone VARCHAR(20);
ALTER TABLE users ADD COLUMN last_login TIMESTAMP NULL;
-- Modify column
ALTER TABLE users MODIFY COLUMN phone VARCHAR(25);
-- PostgreSQL
ALTER TABLE users ALTER COLUMN phone TYPE VARCHAR(25);
-- Drop column
ALTER TABLE users DROP COLUMN phone;
-- Add constraint
ALTER TABLE users ADD CONSTRAINT uk_email UNIQUE (email);
ALTER TABLE orders ADD CONSTRAINT fk_orders_user_id
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
-- Drop constraint
ALTER TABLE users DROP CONSTRAINT uk_email;
ALTER TABLE orders DROP CONSTRAINT fk_orders_user_id;
-- Rename table
RENAME TABLE old_table TO new_table;
-- PostgreSQL
ALTER TABLE old_table RENAME TO new_table;
-- Rename column
ALTER TABLE users CHANGE old_column new_column VARCHAR(50);
-- PostgreSQL
ALTER TABLE users RENAME COLUMN old_column TO new_column;
Indexverwaltung¶
-- Create indexes
CREATE INDEX idx_users_email ON users(email);
CREATE UNIQUE INDEX idx_users_username ON users(username);
CREATE INDEX idx_users_name ON users(first_name, last_name);
-- Composite indexes
CREATE INDEX idx_orders_user_status ON orders(user_id, status);
CREATE INDEX idx_orders_date_status ON orders(created_at, status);
-- Partial indexes (PostgreSQL)
CREATE INDEX idx_active_users ON users(email) WHERE is_active = true;
CREATE INDEX idx_recent_orders ON orders(created_at)
WHERE created_at >= NOW() - INTERVAL '30 days';
-- Functional indexes
CREATE INDEX idx_users_lower_email ON users(LOWER(email));
CREATE INDEX idx_orders_year ON orders(EXTRACT(YEAR FROM created_at));
-- Full-text indexes (MySQL)
CREATE FULLTEXT INDEX idx_products_search ON products(name, description);
-- GIN indexes for JSON (PostgreSQL)
CREATE INDEX idx_users_profile_gin ON users USING GIN (profile_data);
-- Show indexes
SHOW INDEXES FROM users;
-- PostgreSQL
SELECT * FROM pg_indexes WHERE tablename = 'users';
-- Drop index
DROP INDEX idx_users_email ON users;
-- PostgreSQL
DROP INDEX idx_users_email;
-- Index usage statistics (PostgreSQL)
SELECT
schemaname,
tablename,
indexname,
idx_scan,
idx_tup_read,
idx_tup_fetch
FROM pg_stat_user_indexes
ORDER BY idx_scan DESC;
Datenoperationen¶
Dateneinfügung¶
-- Single row insert
INSERT INTO users (username, email, password_hash, first_name, last_name)
VALUES ('john_doe', 'john@example.com', 'hashed_password', 'John', 'Doe');
-- Multiple row insert
INSERT INTO users (username, email, password_hash, first_name, last_name) VALUES
('jane_smith', 'jane@example.com', 'hashed_password', 'Jane', 'Smith'),
('bob_wilson', 'bob@example.com', 'hashed_password', 'Bob', 'Wilson'),
('alice_brown', 'alice@example.com', 'hashed_password', 'Alice', 'Brown');
-- Insert with JSON data (MySQL 5.7+)
INSERT INTO users (username, email, profile_data) VALUES
('user1', 'user1@example.com', '{"preferences": {"theme": "dark", "language": "en"}, "settings": {"notifications": true}}');
-- Insert with JSONB (PostgreSQL)
INSERT INTO orders (user_id, order_number, order_data) VALUES
(1, 'ORD-000001', '{"items": [{"product_id": 1, "quantity": 2, "price": 29.99}], "shipping": {"method": "standard", "cost": 5.99}}');
-- Insert from SELECT
INSERT INTO archived_orders (user_id, order_number, total_amount, created_at)
SELECT user_id, order_number, total_amount, created_at
FROM orders
WHERE created_at < DATE_SUB(NOW(), INTERVAL 1 YEAR);
-- Insert with ON DUPLICATE KEY UPDATE (MySQL)
INSERT INTO user_stats (user_id, login_count, last_login)
VALUES (1, 1, NOW())
ON DUPLICATE KEY UPDATE
login_count = login_count + 1,
last_login = NOW();
-- Insert with UPSERT (PostgreSQL)
INSERT INTO user_stats (user_id, login_count, last_login)
VALUES (1, 1, NOW())
ON CONFLICT (user_id) DO UPDATE SET
login_count = user_stats.login_count + 1,
last_login = NOW();
Datenaktualisierung¶
Note: I've translated the text content while preserving the markdown formatting, keeping technical terms in English, and maintaining the same structure and punctuation. Placeholders for sections 4-20 are left blank as no specific text was provided for translation.```sql -- Simple update UPDATE users SET email = 'newemail@example.com' WHERE id = 1;
-- Update with conditions UPDATE users SET is_active = FALSE, updated_at = NOW() WHERE last_login < DATE_SUB(NOW(), INTERVAL 6 MONTH);
-- Update with JOIN UPDATE users u JOIN user_profiles p ON u.id = p.user_id SET u.first_name = p.display_name WHERE p.display_name IS NOT NULL;
-- Update JSON data (MySQL) UPDATE users SET profile_data = JSON_SET(profile_data, '$.preferences.theme', 'light') WHERE id = 1;
-- Update JSONB data (PostgreSQL) UPDATE orders SET order_data = order_data || '{"status": "updated"}'::jsonb WHERE id = 1;
-- Conditional update UPDATE products SET price = CASE WHEN category = 'electronics' THEN price * 1.1 WHEN category = 'books' THEN price * 1.05 ELSE price END WHERE updated_at < DATE_SUB(NOW(), INTERVAL 1 MONTH);
-- Update with subquery UPDATE users SET total_orders = ( SELECT COUNT(*) FROM orders WHERE orders.user_id = users.id );
-- Bulk update with temporary table CREATE TEMPORARY TABLE temp_updates ( user_id INT, new_email VARCHAR(100) );
INSERT INTO temp_updates VALUES (1, 'user1_new@example.com'), (2, 'user2_new@example.com');
UPDATE users u JOIN temp_updates t ON u.id = t.user_id SET u.email = t.new_email; ```### Datenlöschung
-- Simple delete
DELETE FROM users WHERE id = 1;
-- Delete with conditions
DELETE FROM logs
WHERE created_at < DATE_SUB(NOW(), INTERVAL 30 DAY);
-- Delete with JOIN
DELETE u FROM users u
JOIN user_profiles p ON u.id = p.user_id
WHERE p.status = 'deleted';
-- Delete with subquery
DELETE FROM orders
WHERE user_id IN (
SELECT id FROM users WHERE is_active = FALSE
);
-- Safe delete with LIMIT
DELETE FROM temp_data
WHERE processed = TRUE
ORDER BY created_at
LIMIT 1000;
-- Soft delete (mark as deleted)
UPDATE users
SET is_deleted = TRUE, deleted_at = NOW()
WHERE id = 1;
-- Cascade delete (with foreign key constraints)
DELETE FROM users WHERE id = 1;
-- This will also delete related orders if FK has ON DELETE CASCADE
-- Truncate table (fast delete all)
TRUNCATE TABLE temp_table;
-- Delete duplicates
DELETE u1 FROM users u1
INNER JOIN users u2
WHERE u1.id > u2.id
AND u1.email = u2.email;
-- PostgreSQL delete duplicates
DELETE FROM users
WHERE id NOT IN (
SELECT MIN(id)
FROM users
GROUP BY email
);
```### Datenabfrage
```sql
-- Basic SELECT
SELECT id, username, email, created_at
FROM users
WHERE is_active = TRUE
ORDER BY created_at DESC
LIMIT 10;
-- Complex WHERE conditions
SELECT * FROM orders
WHERE (status = 'pending' OR status = 'processing')
AND total_amount > 100
AND created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY);
-- JOIN operations
SELECT
u.username,
u.email,
COUNT(o.id) as order_count,
SUM(o.total_amount) as total_spent,
MAX(o.created_at) as last_order_date
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.is_active = TRUE
GROUP BY u.id, u.username, u.email
HAVING COUNT(o.id) > 0
ORDER BY total_spent DESC;
-- Subqueries
SELECT * FROM users
WHERE id IN (
SELECT DISTINCT user_id
FROM orders
WHERE total_amount > 500
);
-- EXISTS subquery
SELECT * FROM users u
WHERE EXISTS (
SELECT 1 FROM orders o
WHERE o.user_id = u.id
AND o.status = 'completed'
);
-- Window functions (PostgreSQL/MySQL 8.0+)
SELECT
username,
email,
created_at,
ROW_NUMBER() OVER (ORDER BY created_at) as user_number,
RANK() OVER (ORDER BY created_at) as user_rank,
LAG(created_at) OVER (ORDER BY created_at) as previous_user_date
FROM users;
-- JSON queries (MySQL)
SELECT
id,
username,
JSON_EXTRACT(profile_data, '$.preferences.theme') as theme,
JSON_EXTRACT(profile_data, '$.settings.notifications') as notifications
FROM users
WHERE JSON_EXTRACT(profile_data, '$.preferences.theme') = 'dark';
-- JSONB queries (PostgreSQL)
SELECT
id,
order_number,
order_data->>'status' as status,
order_data->'items'->0->>'product_id' as first_product_id
FROM orders
WHERE order_data @> '{"status": "completed"}';
-- Full-text search (MySQL)
SELECT * FROM products
WHERE MATCH(name, description) AGAINST('laptop gaming' IN NATURAL LANGUAGE MODE);
-- Full-text search (PostgreSQL)
SELECT * FROM products
WHERE to_tsvector('english', name || ' ' || description) @@ to_tsquery('english', 'laptop & gaming');
```## SQL-Editor
```sql
-- Adminer SQL editor features:
-- 1. Syntax highlighting
-- 2. Auto-completion
-- 3. Query history
-- 4. Multiple query execution
-- 5. Export results
-- 6. Query profiling
-- Execute multiple queries (separate with semicolons)
SELECT COUNT(*) as total_users FROM users;
SELECT COUNT(*) as total_orders FROM orders;
SELECT COUNT(*) as total_products FROM products;
-- Query with parameters (use Adminer's parameter feature)
SELECT * FROM users
WHERE created_at >= ?
AND status = ?;
-- Parameters: ['2023-01-01', 'active']
-- Complex analytical query
WITH monthly_stats AS (
SELECT
DATE_FORMAT(created_at, '%Y-%m') as month,
COUNT(*) as order_count,
SUM(total_amount) as revenue,
AVG(total_amount) as avg_order_value
FROM orders
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 12 MONTH)
GROUP BY DATE_FORMAT(created_at, '%Y-%m')
),
growth_stats AS (
SELECT
month,
order_count,
revenue,
avg_order_value,
LAG(revenue) OVER (ORDER BY month) as prev_month_revenue,
(revenue - LAG(revenue) OVER (ORDER BY month)) / LAG(revenue) OVER (ORDER BY month) * 100 as growth_rate
FROM monthly_stats
)
SELECT
month,
order_count,
ROUND(revenue, 2) as revenue,
ROUND(avg_order_value, 2) as avg_order_value,
ROUND(growth_rate, 2) as growth_rate_percent
FROM growth_stats
ORDER BY month;
```### Abfrageausführung
```sql
-- Use EXPLAIN to analyze query performance
EXPLAIN SELECT
u.username,
COUNT(o.id) as order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.created_at >= '2023-01-01'
GROUP BY u.id, u.username;
-- PostgreSQL EXPLAIN with detailed analysis
EXPLAIN (ANALYZE, BUFFERS, VERBOSE)
SELECT
u.username,
COUNT(o.id) as order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.created_at >= '2023-01-01'
GROUP BY u.id, u.username;
-- Optimize with proper indexes
CREATE INDEX idx_users_created_at ON users(created_at);
CREATE INDEX idx_orders_user_id ON orders(user_id);
-- Use LIMIT for large result sets
SELECT * FROM large_table
ORDER BY created_at DESC
LIMIT 100;
-- Use EXISTS instead of IN for better performance
SELECT * FROM users u
WHERE EXISTS (
SELECT 1 FROM orders o
WHERE o.user_id = u.id
)
LIMIT 1000;
-- Optimize GROUP BY queries
SELECT
status,
COUNT(*) as count
FROM orders
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY status
ORDER BY count DESC;
-- Use covering indexes
CREATE INDEX idx_orders_covering ON orders(status, created_at, total_amount);
-- Query with index hints (MySQL)
SELECT * FROM users
USE INDEX (idx_users_email)
WHERE email = 'user@example.com';
-- Force index usage
SELECT * FROM users
FORCE INDEX (idx_users_created_at)
WHERE created_at >= '2023-01-01'
ORDER BY created_at;
```### Abfrageoptimierung
```sql
-- MySQL stored procedure
DELIMITER //
CREATE PROCEDURE GetUserStats(IN user_id INT)
BEGIN
SELECT
u.username,
u.email,
COUNT(o.id) as total_orders,
SUM(o.total_amount) as total_spent,
AVG(o.total_amount) as avg_order_value,
MAX(o.created_at) as last_order_date
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.id = user_id
GROUP BY u.id, u.username, u.email;
END //
DELIMITER ;
-- Call stored procedure
CALL GetUserStats(1);
-- MySQL function
DELIMITER //
CREATE FUNCTION CalculateDiscount(order_total DECIMAL(10,2))
RETURNS DECIMAL(10,2)
READS SQL DATA
DETERMINISTIC
BEGIN
DECLARE discount DECIMAL(10,2) DEFAULT 0;
IF order_total >= 1000 THEN
SET discount = order_total * 0.10;
ELSEIF order_total >= 500 THEN
SET discount = order_total * 0.05;
ELSEIF order_total >= 100 THEN
SET discount = order_total * 0.02;
END IF;
RETURN discount;
END //
DELIMITER ;
-- Use function
SELECT
id,
total_amount,
CalculateDiscount(total_amount) as discount,
total_amount - CalculateDiscount(total_amount) as final_amount
FROM orders;
-- PostgreSQL function
CREATE OR REPLACE FUNCTION get_user_order_summary(p_user_id INTEGER)
RETURNS TABLE(
username VARCHAR,
email VARCHAR,
total_orders BIGINT,
total_spent NUMERIC,
avg_order_value NUMERIC
) AS $$
BEGIN
RETURN QUERY
SELECT
u.username,
u.email,
COUNT(o.id)::BIGINT,
COALESCE(SUM(o.total_amount), 0),
COALESCE(AVG(o.total_amount), 0)
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.id = p_user_id
GROUP BY u.username, u.email;
END;
$$ LANGUAGE plpgsql;
-- Call PostgreSQL function
SELECT * FROM get_user_order_summary(1);
```### Gespeicherte Prozeduren und Funktionen
```php
<?php
// CSV import configuration in Adminer
$import_config = [
'format' => 'csv',
'delimiter' => ',',
'enclosure' => '"',
'escape' => '\\',
'charset' => 'utf-8',
'skip_first_line' => true,
'null_value' => '',
'date_format' => 'Y-m-d',
'datetime_format' => 'Y-m-d H:i:s'
];
// SQL import example
$sql_import = "
INSERT INTO users (username, email, first_name, last_name) VALUES
('user1', 'user1@example.com', 'John', 'Doe'),
('user2', 'user2@example.com', 'Jane', 'Smith'),
('user3', 'user3@example.com', 'Bob', 'Wilson');
INSERT INTO orders (user_id, order_number, total_amount) VALUES
(1, 'ORD-001', 99.99),
(2, 'ORD-002', 149.99),
(3, 'ORD-003', 79.99);
";
?>
-- Bulk import strategies
-- MySQL LOAD DATA INFILE
LOAD DATA INFILE '/path/to/users.csv'
INTO TABLE users
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 ROWS
(username, email, first_name, last_name, @created_at)
SET created_at = STR_TO_DATE(@created_at, '%m/%d/%Y %H:%i:%s');
-- PostgreSQL COPY
COPY users(username, email, first_name, last_name, created_at)
FROM '/path/to/users.csv'
DELIMITER ','
CSV HEADER;
-- Import with data transformation
LOAD DATA INFILE '/path/to/orders.csv'
INTO TABLE orders
FIELDS TERMINATED BY ','
LINES TERMINATED BY '\n'
IGNORE 1 ROWS
(user_id, @order_date, @total_amount, status)
SET
order_date = STR_TO_DATE(@order_date, '%m/%d/%Y'),
total_amount = CAST(@total_amount AS DECIMAL(10,2)),
created_at = NOW();
-- Handle import errors
LOAD DATA INFILE '/path/to/data.csv'
INTO TABLE temp_import
FIELDS TERMINATED BY ','
LINES TERMINATED BY '\n'
IGNORE 1 ROWS;
-- Validate imported data
SELECT COUNT(*) as total_imported FROM temp_import;
SELECT COUNT(*) as invalid_emails
FROM temp_import
WHERE email NOT REGEXP '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$';
-- Transfer valid data
INSERT INTO users (username, email, first_name, last_name)
SELECT username, email, first_name, last_name
FROM temp_import
WHERE email REGEXP '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$'
AND username IS NOT NULL
AND username != '';
```## Import/Export
```sql
-- Export formats available in Adminer:
-- 1. SQL - INSERT statements
-- 2. CSV - Comma-separated values
-- 3. TSV - Tab-separated values
-- 4. XML - XML format
-- 5. JSON - JSON format
-- Export with custom query
SELECT
u.id,
u.username,
u.email,
u.first_name,
u.last_name,
COUNT(o.id) as total_orders,
SUM(o.total_amount) as total_spent,
DATE_FORMAT(u.created_at, '%Y-%m-%d') as registration_date
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.is_active = TRUE
GROUP BY u.id
ORDER BY total_spent DESC;
-- Export specific date range
SELECT * FROM orders
WHERE created_at >= '2023-01-01'
AND created_at < '2024-01-01'
ORDER BY created_at;
-- Export with data transformation
SELECT
id,
username,
CONCAT(first_name, ' ', last_name) as full_name,
email,
CASE
WHEN is_active = 1 THEN 'Active'
ELSE 'Inactive'
END as status,
DATE_FORMAT(created_at, '%m/%d/%Y') as registration_date
FROM users;
-- Export JSON data (MySQL)
SELECT
id,
username,
email,
JSON_PRETTY(profile_data) as profile_json
FROM users
WHERE profile_data IS NOT NULL;
-- Export aggregated data
SELECT
DATE_FORMAT(created_at, '%Y-%m') as month,
COUNT(*) as order_count,
SUM(total_amount) as total_revenue,
AVG(total_amount) as avg_order_value,
MIN(total_amount) as min_order,
MAX(total_amount) as max_order
FROM orders
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 12 MONTH)
GROUP BY DATE_FORMAT(created_at, '%Y-%m')
ORDER BY month;
```### Datenimport
```php
<?php
// Automated backup script using Adminer
class AdminerBackup extends Adminer {
function dumpTable($table, $style, $is_view = 0) {
if ($style == 'CREATE') {
// Add backup timestamp
echo "-- Backup created: " . date('Y-m-d H:i:s') . "\n";
echo "-- Table: $table\n\n";
}
return parent::dumpTable($table, $style, $is_view);
}
function dumpData($table, $style, $query) {
if ($style == 'INSERT') {
echo "-- Data for table: $table\n";
}
return parent::dumpData($table, $style, $query);
}
}
// Backup configuration
$backup_config = [
'format' => 'sql',
'include_structure' => true,
'include_data' => true,
'include_drop' => true,
'include_create' => true,
'add_locks' => true,
'hex_blob' => true,
'routines' => true,
'events' => true,
'triggers' => true
];
?>
```bash
Command-line backup scripts¶
MySQL backup¶
!/bin/bash¶
DATE=$(date +%Y%m%d_%H%M%S) BACKUP_DIR="/backups" DB_NAME="myapp"
mysqldump -u backup_user -p$BACKUP_PASSWORD \ --single-transaction \ --routines \ --triggers \ --events \ --hex-blob \ --add-drop-table \ --add-locks \ \(DB_NAME > \(BACKUP_DIR/\)_\).sql
Compress backup¶
gzip \(BACKUP_DIR/\).sql}_${DATE
Keep only last 7 days¶
find \(BACKUP_DIR -name "\)_*.sql.gz" -mtime +7 -delete
PostgreSQL backup¶
!/bin/bash¶
DATE=$(date +%Y%m%d_%H%M%S) BACKUP_DIR="/backups" DB_NAME="myapp"
pg_dump -U backup_user -h localhost \ --verbose \ --clean \ --create \ --if-exists \ --format=custom \ --compress=9 \ \(DB_NAME > \(BACKUP_DIR/\)_\).backup
Keep only last 7 days¶
find \(BACKUP_DIR -name "\)_*.backup" -mtime +7 -delete
Restore MySQL¶
mysql -u username -p myapp < backup_file.sql
Restore PostgreSQL¶
pg_restore -U username -h localhost -d myapp backup_file.backup
### Datenexportsql
-- MySQL user management
-- Create user
CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'secure_password';
CREATE USER 'app_user'@'%' IDENTIFIED BY 'secure_password';
-- Grant privileges GRANT SELECT, INSERT, UPDATE, DELETE ON myapp.* TO 'app_user'@'localhost'; GRANT ALL PRIVILEGES ON myapp.* TO 'admin_user'@'localhost';
-- Create role (MySQL 8.0+) CREATE ROLE 'app_role'; GRANT SELECT, INSERT, UPDATE ON myapp.* TO 'app_role'; GRANT 'app_role' TO 'app_user'@'localhost';
-- Set default role SET DEFAULT ROLE 'app_role' TO 'app_user'@'localhost';
-- Show user privileges SHOW GRANTS FOR 'app_user'@'localhost';
-- Change password ALTER USER 'app_user'@'localhost' IDENTIFIED BY 'new_password';
-- Account locking/unlocking ALTER USER 'app_user'@'localhost' ACCOUNT LOCK; ALTER USER 'app_user'@'localhost' ACCOUNT UNLOCK;
-- Password expiration ALTER USER 'app_user'@'localhost' PASSWORD EXPIRE; ALTER USER 'app_user'@'localhost' PASSWORD EXPIRE NEVER;
-- Drop user DROP USER 'app_user'@'localhost';
-- PostgreSQL user management -- Create user CREATE USER app_user WITH PASSWORD 'secure_password'; CREATE USER readonly_user WITH PASSWORD 'readonly_password';
-- Create role CREATE ROLE app_role; CREATE ROLE readonly_role;
-- Grant privileges to role GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_role; GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly_role;
-- Grant role to user GRANT app_role TO app_user; GRANT readonly_role TO readonly_user;
-- Grant database privileges GRANT CONNECT ON DATABASE myapp TO app_user; GRANT USAGE ON SCHEMA public TO app_user;
-- Change password ALTER USER app_user WITH PASSWORD 'new_password';
-- Drop user DROP USER app_user;
-- Show user information
SELECT
usename as username,
usesuper as is_superuser,
usecreatedb as can_create_db,
userepl as can_replicate,
valuntil as password_expiry
FROM pg_user;
### Sicherung und Wiederherstellungsql
-- Granular permission management
-- Table-level permissions GRANT SELECT ON users TO 'readonly_user'@'localhost'; GRANT INSERT, UPDATE ON orders TO 'app_user'@'localhost'; GRANT DELETE ON logs TO 'admin_user'@'localhost';
-- Column-level permissions (MySQL 8.0+) GRANT SELECT (id, username, email) ON users TO 'limited_user'@'localhost'; GRANT UPDATE (email, updated_at) ON users TO 'profile_editor'@'localhost';
-- PostgreSQL column-level permissions GRANT SELECT (id, username, email) ON users TO limited_user; GRANT UPDATE (email, updated_at) ON users TO profile_editor;
-- Stored procedure permissions GRANT EXECUTE ON PROCEDURE GetUserStats TO 'app_user'@'localhost'; GRANT EXECUTE ON FUNCTION CalculateDiscount TO 'app_user'@'localhost';
-- View permissions CREATE VIEW user_summary AS SELECT id, username, email, created_at FROM users WHERE is_active = TRUE;
GRANT SELECT ON user_summary TO 'readonly_user'@'localhost';
-- Row-level security (PostgreSQL) ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
CREATE POLICY user_orders_policy ON orders FOR ALL TO app_user USING (user_id = current_setting('app.current_user_id')::INTEGER);
-- Set user context SET app.current_user_id = '123';
-- Revoke permissions REVOKE INSERT, UPDATE ON orders FROM 'app_user'@'localhost'; REVOKE ALL PRIVILEGES ON myapp.* FROM 'old_user'@'localhost';
-- Show effective permissions
SELECT
grantee,
table_schema,
table_name,
privilege_type,
is_grantable
FROM information_schema.table_privileges
WHERE grantee = 'app_user@localhost';
## Benutzerverwaltungsql
-- Password policies (MySQL 8.0+)
-- Install password validation plugin
INSTALL PLUGIN validate_password SONAME 'validate_password.so';
-- Configure password policy SET GLOBAL validate_password.policy = STRONG; SET GLOBAL validate_password.length = 12; SET GLOBAL validate_password.mixed_case_count = 1; SET GLOBAL validate_password.number_count = 1; SET GLOBAL validate_password.special_char_count = 1;
-- Account management policies CREATE USER 'temp_user'@'localhost' IDENTIFIED BY 'TempPassword123!' PASSWORD EXPIRE INTERVAL 30 DAY FAILED_LOGIN_ATTEMPTS 3 PASSWORD_LOCK_TIME 2;
-- Connection limits ALTER USER 'app_user'@'localhost' WITH MAX_CONNECTIONS_PER_HOUR 1000 MAX_QUERIES_PER_HOUR 10000 MAX_UPDATES_PER_HOUR 5000;
-- PostgreSQL security policies -- Create security-focused role CREATE ROLE secure_app_role NOLOGIN NOCREATEDB NOCREATEROLE NOREPLICATION;
-- Grant minimal required privileges GRANT CONNECT ON DATABASE myapp TO secure_app_role; GRANT USAGE ON SCHEMA public TO secure_app_role; GRANT SELECT, INSERT, UPDATE ON users TO secure_app_role; GRANT SELECT ON products TO secure_app_role;
-- Create application user with secure role CREATE USER app_user WITH PASSWORD 'SecurePassword123!' IN ROLE secure_app_role VALID UNTIL '2024-12-31';
-- Audit user activities CREATE TABLE user_audit_log ( id SERIAL PRIMARY KEY, username VARCHAR(50), action VARCHAR(50), table_name VARCHAR(50), record_id INTEGER, old_values JSONB, new_values JSONB, timestamp TIMESTAMP DEFAULT NOW(), ip_address INET );
-- Audit trigger function CREATE OR REPLACE FUNCTION audit_trigger_function() RETURNS TRIGGER AS $$ BEGIN INSERT INTO user_audit_log ( username, action, table_name, record_id, old_values, new_values, ip_address ) VALUES ( current_user, TG_OP, TG_TABLE_NAME, COALESCE(NEW.id, OLD.id), CASE WHEN TG_OP = 'DELETE' THEN to_jsonb(OLD) ELSE NULL END, CASE WHEN TG_OP = 'INSERT' THEN to_jsonb(NEW) WHEN TG_OP = 'UPDATE' THEN to_jsonb(NEW) ELSE NULL END, inet_client_addr() );
RETURN COALESCE(NEW, OLD);
END; $$ LANGUAGE plpgsql;
-- Apply audit trigger to sensitive tables
CREATE TRIGGER users_audit_trigger
AFTER INSERT OR UPDATE OR DELETE ON users
FOR EACH ROW EXECUTE FUNCTION audit_trigger_function();
### Benutzererstellung und -verwaltungphp
### Berechtigungsverwaltungphp
### Sicherheitsrichtlinienapache
Apache SSL configuration for Adminer¶
SSLEngine on
SSLCertificateFile /etc/ssl/certs/adminer.crt
SSLCertificateKeyFile /etc/ssl/private/adminer.key
SSLCertificateChainFile /etc/ssl/certs/ca-bundle.crt
# Strong SSL configuration
SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
SSLHonorCipherOrder on
# Security headers
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header always set X-Content-Type-Options nosniff
Header always set X-Frame-Options DENY
Header always set X-XSS-Protection "1; mode=block"
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'"
# Client certificate authentication (optional)
SSLVerifyClient require
SSLVerifyDepth 2
SSLCACertificateFile /etc/ssl/certs/client-ca.crt
<Directory /var/www/adminer>
Options -Indexes
AllowOverride All
Require all granted
# Additional security
php_admin_value open_basedir "/var/www/adminer:/tmp"
php_admin_flag allow_url_fopen off
php_admin_flag allow_url_include off
</Directory>
## Sicherheitphp
### Zugangskontrollephp
Query Performance Statistics
'; echo '| Time | Query | Execution Time | Rows |
|---|---|---|---|
| ' . h($log['timestamp']) . ' | '; echo '' . h($query_preview) . ' | '; echo '' . number_format($log['execution_time'], 4) . 's | '; echo '' . number_format($log['rows_affected']) . ' | '; echo '
Total Queries: ' . count($this->query_log) . ' | '; echo 'Total Time: ' . number_format($total_time, 4) . 's | '; echo 'Average Time: ' . number_format($avg_time, 4) . 's
'; echo '### Datenschutzphp
### SSL/TLS-Konfigurationphp
## Anpassungbash
!/bin/bash¶
Adminer maintenance script¶
Configuration¶
ADMINER_DIR="/var/www/adminer" LOG_DIR="/var/log/adminer" BACKUP_DIR="/backups/adminer" DATE=$(date +%Y%m%d_%H%M%S)
Create necessary directories¶
mkdir -p $LOG_DIR $BACKUP_DIR
Log rotation¶
find $LOG_DIR -name ".log" -size +100M -exec gzip {} \; find $LOG_DIR -name ".log.gz" -mtime +30 -delete
Security scan¶
echo "Running security scan..." grep -i "error|warning|failed" $LOG_DIR/audit.log | tail -20
Check for suspicious activity¶
echo "Checking for suspicious activity..." awk '{print $1}' $LOG_DIR/audit.log | sort | uniq -c | sort -nr | head -10
Performance monitoring¶
echo "Performance statistics:" awk '/execution_time/ {sum+=$2; count++} END {print "Average query time:", sum/count "s"}' $LOG_DIR/slow_queries.log
Disk space check¶
echo "Disk space usage:" df -h $ADMINER_DIR du -sh $LOG_DIR
Update check¶
echo "Checking for Adminer updates..." CURRENT_VERSION=$(grep -o 'version.*[0-9]+.[0-9]+.[0-9]+' $ADMINER_DIR/adminer.php | head -1) echo "Current version: $CURRENT_VERSION"
Backup configuration¶
echo "Creating configuration backup..." tar -czf \(BACKUP_DIR/adminer_config_\)DATE.tar.gz $ADMINER_DIR/*.php
Clean old backups¶
find $BACKUP_DIR -name "adminer_config_*.tar.gz" -mtime +7 -delete
echo "Maintenance completed at $(date)" ```### Themes und Styling