MySQL Select Data

sa sahaya jerish Updated 20 Oct 2026
6 min read ·Lesson 152 of 155
In this post

Reading Data From a Table In simple terms: Most of what a website does is read data: listing products, showing a customer's orders, displaying a blog post. SQL's SELECT statement asks the database for

Reading Data From a Table

In simple terms: Most of what a website does is read data: listing products, showing a customer's orders, displaying a blog post. SQL's SELECT statement asks the database for rows, and PHP then turns those rows into arrays or objects to display.

Technically: SELECT columns FROM table returns a result set. In MySQLi, query() and execute_query() return a mysqli_result object for SELECT statements. Rows are fetched one at a time (fetch_assoc(), fetch_object(), fetch_row()), all at once (fetch_all()), as a single value (fetch_column()), or by iterating the result with foreach.

The examples use the five products inserted in MySQL Insert Multiple.


The SELECT Statement

SELECT sku, name, price, stock
FROM products
ORDER BY id;

 

PartMeaning
SELECT sku, name, price, stockThe columns to return
FROM productsThe table to read from
ORDER BY idThe order of the rows (see MySQL Order By)

 

Filtering with WHERE is covered in MySQL Where, limiting the number of rows in MySQL Limit Data.

Select Only What You Need

SELECT * returns every column. Listing columns explicitly is better:

  • Less data travels from the database to PHP.
  • Code does not break or change behavior when columns are added later.
  • Sensitive columns (such as password hashes) are not fetched by accident.

Fetching Rows One by One: fetch_assoc()

<?php
require __DIR__ . '/src/db.php';

$result = db()->query('SELECT sku, name, price, stock FROM products ORDER BY id');

printf("%-9s %-15s %9s %5s\n", 'SKU', 'Name', 'Price', 'Stock');

while ($row = $result->fetch_assoc()) {
    printf("%-9s %-15s %9s %5d\n", $row['sku'], $row['name'], $row['price'], $row['stock']);
}

echo $result->num_rows . " products\n";

 

Output

SKU       Name                Price Stock
PEN-01    Gel Pen             20.00   120
NB-A5     Notebook A5         60.00    45
LAMP-01   Desk Lamp         1299.00     8
BAG-02    Backpack           899.00     0
MOUSE-01  Wireless Mouse     599.00    25
5 products

 

How It Works

  1. query() sends the SQL (no user input is involved, so a prepared statement is not required) and returns a mysqli_result.
  2. Each call to fetch_assoc() returns the next row as an associative array (['sku' => 'PEN-01', ...]). After the last row, it returns null, which ends the while loop.
  3. num_rows tells how many rows the result contains.

By default, MySQLi buffers the whole result in PHP memory, which is why num_rows is available immediately.


Other Ways to Fetch

<?php
require __DIR__ . '/src/db.php';

$db = db();

// All rows at once
$products = $db->query('SELECT sku, name FROM products ORDER BY id')->fetch_all(MYSQLI_ASSOC);
echo count($products) . " rows; first: {$products[0]['name']}\n";

// Iterate the result directly
foreach ($db->query("SELECT name FROM products WHERE category = 'stationery' ORDER BY id") as $row) {
    echo "- {$row['name']}\n";
}

// A single value
echo $db->query('SELECT name FROM products WHERE id = 3')->fetch_column() . "\n";

// A row as an object
$product = $db->query('SELECT sku, name FROM products WHERE id = 5')->fetch_object();
echo "{$product->sku}: {$product->name}\n";

 

Output

5 rows; first: Gel Pen
- Gel Pen
- Notebook A5
Desk Lamp
MOUSE-01: Wireless Mouse

 

MethodReturns
fetch_assoc()Next row as an associative array, or null
fetch_row()Next row as a numerically indexed array
fetch_object($class = 'stdClass')Next row as an object
fetch_all(MYSQLI_ASSOC)All remaining rows as an array of associative arrays
fetch_column($column = 0)A single value from the next row (PHP 8.1+)
foreach ($result as $row)Iterates rows as associative arrays (PHP 8.0+)

 

fetch_all() is convenient for small results; for large results, loop with fetch_assoc() or foreach to process one row at a time.


Data Types of Fetched Values

<?php
require __DIR__ . '/src/db.php';

$summary = db()->query(
    'SELECT COUNT(*) AS products, SUM(stock) AS units, SUM(price * stock) AS stock_value FROM products'
)->fetch_assoc();

var_dump($summary);

printf(
    "%d products, %d units, stock value ₹%s\n",
    $summary['products'],
    $summary['units'],
    number_format((float) $summary['stock_value'], 2)
);

 

Output

array(3) {
  ["products"]=>
  string(1) "5"
  ["units"]=>
  string(3) "198"
  ["stock_value"]=>
  string(8) "30467.00"
}
5 products, 198 units, stock value ₹30,467.00

 

With query(), MySQLi uses MySQL's text protocol, and every value arrives as a string (and NULL as PHP null). Convert values where types matter. Two alternatives:

OptionEffect
Prepared statements (execute_query(), prepare())Integers and floats are returned as native PHP types (see MySQL Prepared)
$db->options(MYSQLI_OPT_INT_AND_FLOAT_NATIVE, true) before connecting (with mysqli_init() and real_connect())Native integers and floats for query() results too

 

DECIMAL columns remain strings in all cases, preserving exact values.


Mapping Rows to Objects

Turning rows into objects of your own classes gives them types and behavior:

<?php
require __DIR__ . '/src/db.php';

final class Product
{
    public function __construct(
        public readonly string $sku,
        public readonly string $name,
        public readonly int $pricePaise,
        public readonly int $stock,
    ) {}

    public static function fromRow(array $row): self
    {
        return new self(
            $row['sku'],
            $row['name'],
            (int) round((float) $row['price'] * 100),
            (int) $row['stock'],
        );
    }

    public function isAvailable(): bool
    {
        return $this->stock > 0;
    }
}

$result = db()->query('SELECT sku, name, price, stock FROM products ORDER BY id');
$products = array_map(Product::fromRow(...), $result->fetch_all(MYSQLI_ASSOC));

$available = array_filter($products, fn(Product $p): bool => $p->isAvailable());

echo count($available) . " of " . count($products) . " products are available\n";

 

Output

4 of 5 products are available

 

The conversion from strings to proper types happens once, in fromRow(). The rest of the application works with typed Product objects (see PHP Static Methods). Repositories and ORMs automate this mapping.


Practical Example: A Product Listing Page

<?php
require __DIR__ . '/../src/db.php';

$result = db()->query("SELECT sku, name, price, stock FROM products WHERE stock > 0 ORDER BY name");

function e(string $value): string
{
    return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}
?>
<table>
    <tr><th>Product</th><th>Price</th><th>Stock</th></tr>
    <?php foreach ($result as $row): ?>
        <tr>
            <td><?= e($row['name']) ?> <small>(<?= e($row['sku']) ?>)</small></td>
            <td>₹<?= e(number_format((float) $row['price'], 2)) ?></td>
            <td><?= (int) $row['stock'] ?></td>
        </tr>
    <?php endforeach; ?>
</table>

 

Output (first two rows of the generated HTML)

<tr>
    <td>Desk Lamp <small>(LAMP-01)</small></td>
    <td>₹1,299.00</td>
    <td>8</td>
</tr>
<tr>
    <td>Gel Pen <small>(PEN-01)</small></td>
    <td>₹20.00</td>
    <td>120</td>
</tr>

 

Why the Escaping Matters

Data from the database is still untrusted for HTML output: product names were entered by people and could contain <script> tags. Every value is escaped with htmlspecialchars() when printed (see Escaping Output and XSS Prevention).


Large Result Sets

For reports or exports with many rows, buffering the whole result in PHP memory is wasteful. An unbuffered query streams rows from the server:

<?php
$result = db()->query('SELECT id, email FROM customers', MYSQLI_USE_RESULT);

foreach ($result as $row) {
    // process one row at a time, e.g. write it to a CSV file
}

$result->free();

 

With MYSQLI_USE_RESULT, num_rows is not known in advance, and no other query can be run on the connection until all rows are read and the result is freed.


Common Mistakes

Using SELECT *

Effect: Unneeded data, accidental exposure of sensitive columns, and fragile code.

Fix: List the required columns.

Forgetting That Values Are Strings

Effect: Strict comparisons such as $row['stock'] === 0 fail with query() results.

Fix: Convert values ((int), (float)) or use prepared statements.

Fetching Everything With fetch_all() for Large Tables

Effect: High memory usage.

Fix: Iterate row by row, or use unbuffered queries.

Printing Database Values Without Escaping

Effect: Stored XSS attacks.

Fix: Escape every value in HTML output.


Best Practices

  • Select only the columns you need, and always specify the order.
  • Use query() for fixed SQL and prepared statements whenever values come from outside.
  • Choose the fetch method that fits: fetch_assoc()/foreach for loops, fetch_column() for single values, fetch_all() for small sets.
  • Convert rows into typed objects at the data-access boundary.
  • Escape all database values when rendering HTML.

Official reference: https\://www.php.net/manual/en/mysqli-result.fetch-assoc.php

0 Comments

Reviewed before they appear

No comments yet.

PHP
PHP Home PHP Switch PHP Match Alternative Syntax PHP Loops While Loop Do While Loop For Loop Foreach Loop Nested Loops Break Statement PHP Home PHP Intro PHP Install PHP Built-in Web Server PHP Syntax PHP Tags PHP and HTML PHP Comments PHP Multiline Comments PHP Variables Variables Scope PHP Casting Logical Operators Null Coalescing Operator Spaceship Operator Operator Precedence Continue Statement Modify Strings PHP If...Else...Elseif PHP If Modify Strings PHP If Operators PHP Functions Concatenate Strings PHP If...Else Slicing Strings User-defined Functions Slicing Strings PHP Shorthand if Escape Characters Default Argument Values PHP Nested if Pass by Reference String Interpolation Named Arguments PHP File Upload Variadic Functions Return Values Search in Strings Multibyte Strings Type Declarations in Functions PHP Numbers PHP Math PHP Floats PHP Random Numbers PHP Constants PHP Properties PHP Methods PHP Constructor Constructor Property Promotion PHP Cookies PHP Destructor PHP Access Modifiers Readonly Properties PHP Sessions PHP Inheritance Method Overriding PHP Filters The final Keyword CSRF Protection PHP Filters Advanced Session Security Secure Cookies PHP Magic Constants File Upload Security PHP Callback Functions PHP Operators Filesystem Security PHP JSON Arithmetic Operators Error Reporting in Production Hiding PHP Assignment Operators PHP Configuration (php.ini) Comparison Operators PHP Command Line (CLI) PHP Exceptions Increment / Decrement Operators Migrating to PHP 8 PHP Form Validation PHP Form Required PHP Form URL/E-mail Error Handling Form Input Sanitizing Escaping Output (htmlspecialchars) Custom Error Handler Checkbox, Radio and Select Inputs Sticky Forms PHP Form Complete PHP Date and Time Date Formatting Error Logging HTTP Headers and Redirects Output Buffering Sending Email PHP cURL Consuming REST APIs PHP Generators AJAX PHP AJAX Database AJAX XML AJAX Live Search AJAX Poll AJAX with JSON Security Introduction Password Hashing SQL Injection Prevention XSS Prevention Garbage Collection Project: Simple Blog PHP What is OOP PHP Classes/Objects The $this Keyword Access Array Items Update Array Items Add Array Items Remove Array Items Sorting Arrays Multidimensional Arrays Array Destructuring Array Spread Operator Search in Arrays Merge Arrays PHP Abstract Classes PHP Interfaces Polymorphism PHP Traits PHP Static Methods PHP Static Properties Late Static Binding Magic Methods Object Cloning MySQL Database MySQL Connect MySQL Create DB MySQL Create Table MySQL Insert Data MySQL Get Last ID MySQL Insert Multiple MySQL Prepared MySQL Select Data MySQL Where Debugging PHP Project: Image Upload Gallery
Ask about this post
AI Ask about this post

Ask questions about MySQL Select Data and get answers drawn from it.

Signed-in readers only.