SQL injection: from UNION to blind time-based
The cause is always the same: user data treated as code. "SELECT * FROM users WHERE id='".$id."'" with $id = 1' OR '1'='1 changes the condition.
UNION: reading other tables
If the query returns output, UNION SELECT attaches a second select. It needs the same column count and compatible types:
' UNION SELECT username, password, NULL FROM users -- -Find the column count with ORDER BY n until it errors.
Blind: when you see nothing
If there is no output but the response changes (a different page) it is boolean: ' AND SUBSTRING(password,1,1)='a. If not even that, time-based measures the delay:
' AND IF(SUBSTRING(user(),1,1)='r', SLEEP(5), 0) -- -Defence
Parametrized queries (prepared statements) always: data never reaches the SQL parser. Whitelists for identifiers (column/table names) that cannot be parametrized. Never trust manual escaping.