SQL Query Examples
Complete examples of executing raw SQL queries using Miko's database layer.
Basic SQL Queries
SELECT Queries
use Miko\Database\DB;
// Simple SELECT
$users = DB::query("SELECT * FROM users");
// SELECT with WHERE
$users = DB::query(
"SELECT * FROM users WHERE IsActive = ? AND Role = ?",
[true, 'admin']
);
// SELECT specific columns
$users = DB::query(
"SELECT Id, Name, Email FROM users WHERE CreatedDate > ?",
['2024-01-01']
);
Named Parameters
// Using named parameters
$user = DB::query(
"SELECT * FROM users WHERE Email = :email AND IsActive = :active",
['email' => 'john@example.com', 'active' => true]
);
INSERT Queries
Single Insert
$affected = DB::execute(
"INSERT INTO users (Name, Email, Password, Role, IsActive) VALUES (?, ?, ?, ?, ?)",
['John Doe', 'john@example.com', password_hash('secret', PASSWORD_DEFAULT), 'user', true]
);
// Get last inserted ID
$userId = DB::lastInsertId();
echo "Created user with ID: $userId";
Multiple Insert
$sql = "INSERT INTO users (Name, Email, Role) VALUES (?, ?, ?), (?, ?, ?), (?, ?, ?)";
$affected = DB::execute($sql, [
'User 1', 'user1@example.com', 'user',
'User 2', 'user2@example.com', 'user',
'User 3', 'user3@example.com', 'admin'
]);
echo "Inserted $affected rows";
Insert with SELECT
$affected = DB::execute(
"INSERT INTO user_archives (UserId, Name, Email, ArchivedAt)
SELECT Id, Name, Email, NOW() FROM users WHERE IsActive = 0"
);
UPDATE Queries
Simple Update
$affected = DB::execute(
"UPDATE users SET IsActive = ? WHERE Id = ?",
[true, 1]
);
echo "Updated $affected rows";
Update Multiple Columns
$affected = DB::execute(
"UPDATE users SET Name = ?, Email = ?, UpdatedDate = NOW() WHERE Id = ?",
['John Updated', 'john.updated@example.com', 1]
);
Conditional Update
// Update all inactive users who haven't logged in for 1 year
$affected = DB::execute(
"UPDATE users SET Status = 'archived' WHERE IsActive = 0 AND LastLoginAt < ?",
[date('Y-m-d', strtotime('-1 year'))]
);
Update with JOIN
$affected = DB::execute(
"UPDATE orders o
INNER JOIN users u ON o.UserId = u.Id
SET o.Status = 'cancelled'
WHERE u.IsActive = 0"
);
DELETE Queries
Simple Delete
$affected = DB::execute("DELETE FROM sessions WHERE Id = ?", [123]);
Conditional Delete
// Delete expired sessions
$affected = DB::execute(
"DELETE FROM sessions WHERE ExpiredAt < ?",
[date('Y-m-d H:i:s')]
);
echo "Deleted $affected expired sessions";
Delete with Limit
// Delete oldest 1000 log entries
$affected = DB::execute(
"DELETE FROM logs ORDER BY CreatedAt ASC LIMIT 1000"
);
Scalar Values
Get single values from queries:
// Count
$count = DB::scalar("SELECT COUNT(*) FROM users WHERE IsActive = ?", [true]);
echo "Active users: $count";
// Sum
$total = DB::scalar(
"SELECT SUM(TotalAmount) FROM orders WHERE Status = ?",
['completed']
);
echo "Total revenue: $total";
// Single value
$name = DB::scalar("SELECT Name FROM users WHERE Id = ?", [1]);
echo "User name: $name";
// Average
$avgAge = DB::scalar("SELECT AVG(Age) FROM users WHERE Age IS NOT NULL");
// Max/Min
$maxPrice = DB::scalar("SELECT MAX(Price) FROM products");
$minPrice = DB::scalar("SELECT MIN(Price) FROM products WHERE IsActive = 1");
First Row
Get first matching row:
$user = DB::first("SELECT * FROM users WHERE Email = ?", ['john@example.com']);
if ($user) {
echo "Found user: " . $user['Name'];
echo "Email: " . $user['Email'];
} else {
echo "User not found";
}
JOIN Queries
Inner Join
$orders = DB::query(
"SELECT o.*, u.Name as CustomerName, u.Email as CustomerEmail
FROM orders o
INNER JOIN users u ON o.UserId = u.Id
WHERE o.Status = ?
ORDER BY o.CreatedDate DESC",
['pending']
);
foreach ($orders as $order) {
echo "Order #{$order['Id']} - {$order['CustomerName']} - \${$order['TotalAmount']}\n";
}
Left Join
$users = DB::query(
"SELECT u.*, p.Bio, p.Avatar
FROM users u
LEFT JOIN profiles p ON u.Id = p.UserId
WHERE u.IsActive = 1"
);
Multiple Joins
$orderDetails = DB::query(
"SELECT
o.OrderNumber,
o.CreatedDate,
u.Name as CustomerName,
p.Name as ProductName,
oi.Quantity,
oi.UnitPrice,
(oi.Quantity * oi.UnitPrice) as LineTotal
FROM orders o
INNER JOIN users u ON o.UserId = u.Id
INNER JOIN order_items oi ON o.Id = oi.OrderId
INNER JOIN products p ON oi.ProductId = p.Id
WHERE o.Id = ?",
[$orderId]
);
GROUP BY Queries
Simple Grouping
$roleStats = DB::query(
"SELECT Role, COUNT(*) as UserCount
FROM users
WHERE IsActive = 1
GROUP BY Role
ORDER BY UserCount DESC"
);
foreach ($roleStats as $stat) {
echo "{$stat['Role']}: {$stat['UserCount']} users\n";
}
Group with Having
// Find customers who spent more than $1000
$bigSpenders = DB::query(
"SELECT u.Id, u.Name, u.Email, SUM(o.TotalAmount) as TotalSpent
FROM users u
INNER JOIN orders o ON u.Id = o.UserId
WHERE o.Status = 'completed'
GROUP BY u.Id, u.Name, u.Email
HAVING TotalSpent > ?
ORDER BY TotalSpent DESC",
[1000]
);
Multiple Aggregates
$monthlySales = DB::query(
"SELECT
YEAR(CreatedDate) as Year,
MONTH(CreatedDate) as Month,
COUNT(*) as OrderCount,
SUM(TotalAmount) as Revenue,
AVG(TotalAmount) as AvgOrderValue,
MAX(TotalAmount) as MaxOrder,
MIN(TotalAmount) as MinOrder
FROM orders
WHERE Status = 'completed'
GROUP BY YEAR(CreatedDate), MONTH(CreatedDate)
ORDER BY Year DESC, Month DESC
LIMIT 12"
);
Subqueries
WHERE IN Subquery
// Users who have placed orders
$customers = DB::query(
"SELECT * FROM users
WHERE Id IN (SELECT DISTINCT UserId FROM orders)"
);
// Users who haven't placed orders
$nonCustomers = DB::query(
"SELECT * FROM users
WHERE Id NOT IN (SELECT DISTINCT UserId FROM orders)"
);
Correlated Subquery
// Users with their order count
$users = DB::query(
"SELECT u.*,
(SELECT COUNT(*) FROM orders WHERE UserId = u.Id) as OrderCount,
(SELECT SUM(TotalAmount) FROM orders WHERE UserId = u.Id AND Status = 'completed') as TotalSpent
FROM users u
WHERE u.IsActive = 1"
);
Subquery in FROM
$topCustomers = DB::query(
"SELECT * FROM (
SELECT u.Id, u.Name, u.Email, SUM(o.TotalAmount) as TotalSpent
FROM users u
INNER JOIN orders o ON u.Id = o.UserId
WHERE o.Status = 'completed'
GROUP BY u.Id, u.Name, u.Email
) as customer_totals
WHERE TotalSpent > 500
ORDER BY TotalSpent DESC
LIMIT 10"
);
UNION Queries
// Combine results from multiple tables
$notifications = DB::query(
"SELECT Id, Title, 'order' as Type, CreatedAt FROM order_notifications WHERE UserId = ?
UNION ALL
SELECT Id, Title, 'system' as Type, CreatedAt FROM system_notifications WHERE UserId = ?
UNION ALL
SELECT Id, Title, 'promo' as Type, CreatedAt FROM promo_notifications WHERE UserId = ?
ORDER BY CreatedAt DESC
LIMIT 20",
[$userId, $userId, $userId]
);
Transactions
DB::beginTransaction();
try {
// Create order
DB::execute(
"INSERT INTO orders (UserId, OrderNumber, TotalAmount, Status) VALUES (?, ?, ?, ?)",
[$userId, 'ORD-' . time(), 0, 'pending']
);
$orderId = DB::lastInsertId();
// Add order items
$total = 0;
foreach ($items as $item) {
DB::execute(
"INSERT INTO order_items (OrderId, ProductId, Quantity, UnitPrice) VALUES (?, ?, ?, ?)",
[$orderId, $item['product_id'], $item['quantity'], $item['price']]
);
$total += $item['quantity'] * $item['price'];
// Update stock
DB::execute(
"UPDATE products SET Stock = Stock - ? WHERE Id = ?",
[$item['quantity'], $item['product_id']]
);
}
// Update order total
DB::execute("UPDATE orders SET TotalAmount = ? WHERE Id = ?", [$total, $orderId]);
DB::commit();
echo "Order created successfully";
} catch (Exception $e) {
DB::rollback();
echo "Order failed: " . $e->getMessage();
}
Practical Examples
User Authentication
function authenticateUser(string $email, string $password): ?array
{
$user = DB::first(
"SELECT Id, Name, Email, Password, Role, IsActive FROM users WHERE Email = ?",
[$email]
);
if (!$user) {
return null;
}
if (!$user['IsActive']) {
return null;
}
if (!password_verify($password, $user['Password'])) {
return null;
}
// Update last login
DB::execute(
"UPDATE users SET LastLoginAt = NOW() WHERE Id = ?",
[$user['Id']]
);
unset($user['Password']);
return $user;
}
Dashboard Statistics
function getDashboardStats(): array
{
return [
'users' => [
'total' => DB::scalar("SELECT COUNT(*) FROM users"),
'active' => DB::scalar("SELECT COUNT(*) FROM users WHERE IsActive = 1"),
'new_today' => DB::scalar("SELECT COUNT(*) FROM users WHERE DATE(CreatedDate) = CURDATE()")
],
'orders' => [
'total' => DB::scalar("SELECT COUNT(*) FROM orders"),
'pending' => DB::scalar("SELECT COUNT(*) FROM orders WHERE Status = 'pending'"),
'today' => DB::scalar("SELECT COUNT(*) FROM orders WHERE DATE(CreatedDate) = CURDATE()")
],
'revenue' => [
'today' => DB::scalar("SELECT COALESCE(SUM(TotalAmount), 0) FROM orders WHERE Status = 'completed' AND DATE(CreatedDate) = CURDATE()"),
'this_month' => DB::scalar("SELECT COALESCE(SUM(TotalAmount), 0) FROM orders WHERE Status = 'completed' AND YEAR(CreatedDate) = YEAR(CURDATE()) AND MONTH(CreatedDate) = MONTH(CURDATE())"),
'this_year' => DB::scalar("SELECT COALESCE(SUM(TotalAmount), 0) FROM orders WHERE Status = 'completed' AND YEAR(CreatedDate) = YEAR(CURDATE())")
]
];
}
Search with Pagination
function searchProducts(string $query, int $page = 1, int $perPage = 20): array
{
$offset = ($page - 1) * $perPage;
$searchTerm = "%$query%";
// Get total count
$total = DB::scalar(
"SELECT COUNT(*) FROM products WHERE Name LIKE ? OR Description LIKE ?",
[$searchTerm, $searchTerm]
);
// Get results
$products = DB::query(
"SELECT Id, Name, Price, Stock, ImageUrl
FROM products
WHERE Name LIKE ? OR Description LIKE ?
ORDER BY Name
LIMIT ? OFFSET ?",
[$searchTerm, $searchTerm, $perPage, $offset]
);
return [
'items' => $products,
'total' => $total,
'page' => $page,
'per_page' => $perPage,
'total_pages' => ceil($total / $perPage)
];
}
Security Best Practices
// ALWAYS use prepared statements
$users = DB::query("SELECT * FROM users WHERE Email = ?", [$email]);
// NEVER concatenate user input
// BAD: $users = DB::query("SELECT * FROM users WHERE Email = '$email'");
// Validate and sanitize input
$email = filter_var($input, FILTER_VALIDATE_EMAIL);
if (!$email) {
throw new Exception('Invalid email');
}
// Use type casting for numeric values
$id = (int)$_GET['id'];
$user = DB::first("SELECT * FROM users WHERE Id = ?", [$id]);