SQL
SQL
This will create an unique index for the column Email in the table Customers. This index, along with speeding up
queries like a normal index, will also force every email address in that column to be unique. If a row is inserted or
updated with a non-unique Email value, the insertion or update will, by default, fail.
This creates an index on Customers which also creates a table constraint that the EmployeeID must be unique.
(This will fail if the column is not currently unique - in this case, if there are employees who share an ID.)
This creates an index that is sorted in descending order. By default, indexes (in MSSQL server, at least) are
ascending, but that can be changed.
By default rebuilding index is offline operation which locks the table and prevents DML against it , but many RDBM
allow online rebuilding. Also, some DB vendors offer alternatives to index rebuilding such as
REORGANIZE
(SQLServer) orCOALESCE/ SHRINK SPACE(Oracle).
This will fail if an unique index is set on the Email column of Customers. However, alternate behavior can be defin
for this case:
SELECT
ROW_NUMBER() OVER(ORDER BY Fname ASC) AS RowNumber,
Fname,
LName
FROM Employees
SELECT
ROW_NUMBER() OVER(PARTITION BY DepartmentId ORDER BY DepartmentId ASC) AS RowNumber,
DepartmentId, Fname, LName
FROM Employees
SELECT
storeName,
COUNT(*) AS total_nr_orders,
COUNT(DISTINCT userId) AS nr_unique_customers,
AVG(orderValue) AS average_order_value,
MIN(orderDate) AS first_order,
MAX(orderDate) AS lastOrder
FROM
orders
GROUP BY
storeName;
While DISTINCT is used to list a unique combination of distinct values for the specified columns.
SELECT DISTINCT
storeName,
userId
FROM
orders;
storeName userId
Store A 43
Store B 57
Store C 82
Store A 21
This example uses a Common Table Expression and a Window Function to show all duplicate rows (on a subset of
columns) side by side.
Using string functions, you can, for example, combine data, extract a substring, compare strings, or convert a stri
to all uppercase or lowercase characters.
Some databases (e.g., Oracle) perform implicit lossless conversions. For example, a on a CLOBand NCLOB
CONCAT
yields aNCLOB. A CONCATon a number and avarchar2 results in avarchar2 , etc.:
DECLARE @str varchar(100) = 'Hello ' --varchar is usually an ASCII string, occupying 1 byte per
char
SELECT DATALENGTH(@str) -- returns 6
DECLARE @nstr nvarchar(100) = 'Hello ' --nvarchar is a unicode string, occupying 2 bytes per char
SELECT DATALENGTH(@nstr) -- returns 12
Oracle
Examples:
SELECT value FROM STRING_SPLIT('Lorem ipsum dolor sit amet.', ' ');
Result:
value
-----
Lorem
ipsum
dolor
sit
Example:
SELECT REPLACE( 'Peter Steve Tom', 'Steve', 'Billy' ) --Return Values: Peter Billy Tom
Syntax:
Example:
Oracle SQL doesn't have LEFT and RIGHT functions. They can be emulated with SUBSTR and LENGTH.
SUBSTR ( string-expression, 1, integer )
SUBSTR ( string-expression, length(string-expression)-integer+1, integer)
FirstName Address
James South New York
John South Boston
Michael South San Diego
Select Statement :
SELECT
FirstName,
REPLACE (Address, 'South', 'Southern') Address
FROM Employees
ORDER BY FirstName
Result:
Update Statement :
We can use a replace function to make permanent changes in our table through following approach.
Update Employees
Set city = (Address, 'South', 'Southern');
A more common approach is to use this in conjunction with a WHERE clause like this:
Update Employees
Set Address = (Address, 'South', 'Southern')
Where Address LIKE 'South%';
PARSENAME function returns the specific part of given string(object name). object name may contains string like
object name,owner name, database name and server name.
Syntax
PARSENAME('NameOfStringToParse',PartIndex)
Example
PARSENAME will returns null is specified part is not present in given object name string
Result:
Result:
List Concatenation aggregates a column or expression by combining the values into a single string for each group
string to delimit each value (either blank or a comma when omitted) and the order of the values in the result can
specified. While it is not part of the SQL standard, every major relational database vendor supports it in their own
way.
MySQL
SELECT ColumnA
, GROUP_CONCAT(ColumnB ORDER BY ColumnB SEPARATOR ',') AS ColumnBs
FROM TableName
GROUP BY ColumnA
ORDER BY ColumnA;
PostgreSQL
SELECT ColumnA
, STRING_AGG(ColumnB, ',' ORDER BY ColumnB) AS ColumnBs
FROM TableName
GROUP BY ColumnA
ORDER BY ColumnA;
SQL Server
SQL Server 2016 and earlier
WITH CTE_TableName AS (
SELECT ColumnA, ColumnB
FROM TableName)
SELECT t0.ColumnA
, STUFF((
SELECT ',' + t1.ColumnB
FROM CTE_TableName t1
WHERE t1.ColumnA = t0.ColumnA
ORDER BY t1.ColumnB
FOR XML PATH('')), 1, 1, '') AS ColumnBs
FROM CTE_TableName t0
GROUP BY t0.ColumnA
ORDER BY ColumnA;
SQLite
without ordering:
SELECT ColumnA
, GROUP_CONCAT(ColumnB, ',') AS ColumnBs
FROM TableName
GROUP BY ColumnA
ORDER BY ColumnA;
WITH CTE_TableName AS (
SELECT ColumnA, ColumnB
FROM TableName
ORDER BY ColumnA, ColumnB)
SELECT ColumnA
, GROUP_CONCAT(ColumnB, ',') AS ColumnBs
FROM CTE_TableName
GROUP BY ColumnA
ORDER BY ColumnA;
EXAMPLE TABLE
city_name population year
New York City 8,550,405 2015
New York City ... ...
New York City 8,000,906 2005
To select the average population of the New York City, USA from a table containing city names, population
measurements, and measurement years for last ten years:
QUERY
select city_name, AVG(population) avg_population
from city_population
where city_name = 'NEW YORK CITY';
Notice how measurement year is absent from the query since population is being averaged over time.
RESULTS
city_name avg_population
New York City 8,250,754
Note: The AVG() function will convert values to numeric types. This is especially important to keep in mind
when working with dates.
You can count over a column/expression with the effect that will not count thevalues:
NULL
You can also use DISTINCT inside of another function such as COUNT to only find the DISTINCT members of the
set to perform the operation on.
For example:
Will return different values. The SingleCount will only Count individual Continents once, while the AllCount will
include duplicates.
ContinentCode
OC
EU
AS
NA
NA
AF
AF
AllCount: 7 SingleCount: 5
Syntax:
Syntax:
You use scalar functions wherever an expression is allowed within a T-SQL statement.
The DATENAMEfunction returns the name or value of a specific part of the date.
You use theGETDATEfunction to determine the current date and time of the computer running the current SQL
instance. This function doesn't include the time zone difference.
In the syntax, datepart is the parameter that specifies which part of the date you want to use to calculate
difference. The datepart can be year, month, week, day, hour, minute, second, or millisecond. You then specify th
start date in the startdate parameter and the end date in the enddate parameter for which you want to find the
difference.
The lower(char) function converts the given character parameter to be lower-cased characters.
would return the customer's last name changed from "SMITH" to "smith".
In SQL, most data conversions occur implicitly, without any user intervention.
To perform any conversions that can't be completed implicitly, you can use theor CONVERTfunctions.
CAST
The CAST function always uses the default style setting. For example, it will represent dates and times using the
format YYYY-MM-DD.
The CONVERTfunction uses the date and time style you specify. In this case, 3 specifies the date format dd/mm/yy.
USE AdventureWorks2012
GO
SELECT FirstName + ' ' + LastName + ' was hired on ' +
CAST(HireDate AS varchar(20)) AS 'Cast',
FirstName + ' ' + LastName + ' was hired on ' +
CONVERT(varchar, HireDate, 3) AS 'Convert'
FROM Person.Person AS p
JOIN HumanResources.Employee AS e
ON p.BusinessEntityID = e.BusinessEntityID
GO
Cast Convert
David Hamiltion was hired on 2003-02-04 David Hamiltion was hired on 04/02/03
In the syntax for the function, you specify the string that must be converted,
AS the
keyword, and then the required
If the string value can't be converted to a numeric, date, or time format, it will result in an error. You'll then need
use CAST or CONVERTfor the conversion.
The CHOOSEfunction returns an item from a list of values, based on its position in the list. This position is specified
by the index.
In the syntax, the index parameter specifies the item and is a whole number, or integer. The val_1 … val_n
parameter identifies the list of values.
The IIF function returns one of two values, based on a particular condition. If the condition is true, it will return
true value. Otherwise it will return a false value.
In the syntax, the boolean_expression parameter specifies the Boolean expression. The true_value parameter
specifies the value that should be returned if the boolean_expression evaluates to true and the false_value
parameter specifies the value that should be returned if the boolean_expression evaluates to false.
In this example, you use the IIF function to return one of two values. If a sales person's year-to-date sales are abo
200,000, this person will be eligible for a bonus. Values below 200,000 mean that employees don't qualify for
bonuses.
SQL includes several mathematical functions that you can use to perform calculations on input value
return numeric results.
One example is theSIGN function, which returns a value indicating the sign of an expression. The value of -1
indicates a negative expression, the value of +1 indicates a positive expression, and 0 indicates zero.
In the example, the input is a negative number, so the Results pane lists the result -1.
In the syntax, the float_expression parameter specifies the expression, and the y parameter specifies the power t
which you want to raise the expression.
You use a scalar expression to specify the values that should be compared. The offset parameter is the number o
rows before the current row that will be used in the comparison. If you don't specify the number of rows, the
default value of one row is used.
The default parameter specifies the value that should be returned when the expression at offsetNULL
hasvalue.
a If
you don't specify a value, a valueNULL
of is returned.
The LEAD function provides data on rows after the current row in the row set. For example, in a statement,
SELECT
you can compare values in the current row with values in the following row.
You specify the values that should be compared using a scalar expression. The offset parameter is the number of
rows after the current row that will be used in the comparison.
You specify the value that should be returned when the expression at offset NULL
has avalue using the default
parameter. If you don't specify these parameters, the default of one row is used and a value
NULLof
is returned.
This example uses the LEAD and LAG functions to compare the sales values for each employee to date with those
the employees listed above and below, with records ordered based on the BusinessEntityID column.
To find the exact value from the row that matches or exceeds the 0.5 percentile, you pass the percentile as the
numeric literal in the
PERCENTILE_DISC function. The Percentile Discreet column in a result set lists the value of the
row at which the cumulative distribution is higher than the specified percentile.
The first value in the result set always has a percent rank of zero. The value for the highest-ranked – or last – valu
in the set is always one.
The CUME_DISTfunction calculates the relative position of a specified value in a group of values, by determining th
percentage of values less than or equal to that value. This is called the cumulative distribution.
The PERCENT_RANKfunction ranks the entries within each group. For each entry, it returns the percentage of entries
in the same group that have lower values.
The CUME_DISTfunction is similar, except that it returns the percentage of values less than or equal to the current
value.
Table items
id name tag
1 example unique_tag
2 foo simple
42 bar simple
3 baz hello
51 quux world
I'd like to get all those lines and know if a tag is used by other lines
SELECT id, name, tag, COUNT(*) OVER (PARTITION BY tag) > 1 AS flag FROM items
In case your database doesn't have OVER and PARTITION you can use this to produce the same result:
SELECT id, name, tag, (SELECT COUNT(tag) FROM items B WHERE tag = A.tag) > 1 AS flag FROM items A
The LAG() analytical function helps to solve the problem by returning for each row the value in the preceding row:
In case your database doesn't have LAG() you can use this to produce the same result:
date amount
2016-03-12 200
2016-03-11 -50
2016-03-14 100
2016-03-15 100
2016-03-10 -250
SELECT date, amount, SUM(amount) OVER (ORDER BY date ASC) AS running
FROM operations
ORDER BY date ASC
Instead of using two queries to get a count then the line, you can use an aggregate as a window function and use
the full result set as the window.
User_ID Completion_Date
1 2016-07-20
1 2016-07-21
2 2016-07-20
2 2016-07-21
2 2016-07-22
;with CTE as
(SELECT *,
ROW_NUMBER() OVER (PARTITION BY User_ID
ORDER BY Completion_Date DESC) Row_Num
FROM Data)
SELECT * FORM CTE WHERE Row_Num <= n
Using n=1, you'll get the one most recent row user_id
per :
--Give a table name `Numbers" and a column `i` to hold the numbers
WITH Numbers(i) AS (
--Starting number/index
SELECT 1
--Top-level UNION ALL operator required for recursion
UNION ALL
--Iteration expression:
SELECT i + 1
--Table expression we first declared used as source for recursion
FROM Numbers
--Clause to define the end of the recursion
WHERE i < 5
)
--Use the generated table expression like a regular table
SELECT i FROM Numbers;
i
1
2
3
4
5
This method can be used with any number interval, as well as other types of data.
UNION ALL
-- get employees that have any of the previously selected rows as manager
SELECT ManagedByJames.Level + 1,
Employees.ID,
Employees.FName,
Employees.LName
FROM Employees
JOIN ManagedByJames
ON Employees.ManagerID = ManagedByJames.ID
WITH ReadyCars AS (
SELECT *
FROM Cars
WHERE Status = 'READY'
)
SELECT ID, Model, TotalCost
FROM ReadyCars
ORDER BY TotalCost;
ID Model TotalCost
1 Ford F-150 200
2 Ford F-150 230
UNION ALL
-- Transition Sequence = Rest & Relax into Day Shift into Night Shift
-- RR (Rest & Relax) = 1
-- DS (Day Shift) = 2
-- NS (Night Shift) = 3
;WITH roster AS
(
SELECT @DateFrom AS RosterStart, 1 AS TeamA, 2 AS TeamB, 3 AS TeamC
UNION ALL
SELECT DATEADD(d, @IntervalDays, RosterStart),
CASE TeamA WHEN 1 THEN 2 WHEN 2 THEN 3 WHEN 3 THEN 1 END AS TeamA,
CASE TeamB WHEN 1 THEN 2 WHEN 2 THEN 3 WHEN 3 THEN 1 END AS TeamB,
CASE TeamC WHEN 1 THEN 2 WHEN 2 THEN 3 WHEN 3 THEN 1 END AS TeamC
FROM roster WHERE RosterStart < DATEADD(d, -@IntervalDays, @DateTo)
)
SELECT RosterStart,
ISNULL(LEAD(RosterStart) OVER (ORDER BY RosterStart), RosterStart + @IntervalDays) AS
RosterEnd,
CASE TeamA WHEN 1 THEN 'RR' WHEN 2 THEN 'DS' WHEN 3 THEN 'NS' END AS TeamA,
CASE TeamB WHEN 1 THEN 'RR' WHEN 2 THEN 'DS' WHEN 3 THEN 'NS' END AS TeamB,
CASE TeamC WHEN 1 THEN 'RR' WHEN 2 THEN 'DS' WHEN 3 THEN 'NS' END AS TeamC
FROM roster
Result
I.e. For Week 1 TeamA is on R&R, TeamB is on Day Shift and TeamC is on Night Shift.
WITH tbl AS (
SELECT id, name, parent_id
FROM mytable)
, tbl_hierarchy AS (
/* Anchor */
Clauses
CONNECT BY: Specifies the relationship that defines the hierarchy.
START WITH: Specifies the root nodes.
ORDER SIBLINGS BY: Orders results properly.
Parameters
NOCYCLE: Stops processing a branch when a loop is detected. Valid hierarchies are Directed Acyclic
Graphs, and circular references violate this construct.
Operators
PRIOR: Obtains data from the node's parent.
CONNECT_BY_ROOT: Obtains data from the node's root.
Pseudocolumns
LEVEL: Indicates the node's distance from its root.
CONNECT_BY_ISLEAF: Indicates a node without children.
CONNECT_BY_ISCYCLE: Indicates a node with a circular reference.
Functions
SYS_CONNECT_BY_PATH: Returns a flattened/concatenated representation of the path to the node
from its root.
SELECT *
FROM dept_income;
DepartmentName TotalSalary
HR 1900
Sales 600
number
--------
1
(1 row)
number
--------
1
(1 row)
number
--------
1
2
(2 rows)
SELECT *
FROM Employees -- this is a comment
WHERE FName = 'John'
/* This query
returns all employees */
SELECT *
FROM Employees
An example of where a foreign key is required is: In a university, a course must belong to a department. Code for
the this scenario is:
The following table will contain the information of the subjects offered by the Computer science branch:
(The data type of the Foreign Key must match the datatype of the referenced key.)
A Foreign Key must reference a UNIQUE (or PRIMARY) key in the parent table.
Entering a NULL value in a Foreign Key column does not raise an error.
Foreign Key constraints can reference tables within the same database.
Foreign Key constraints can refer to another column in the same table (self-reference).
We will add a new table in order to store the powers of each super hero:
UPDATE Orders
SET Order_UID = orders_seq.NEXTVAL
WHERE Customer = 581;
SELECT *
FROM Employees
WHERE Salary = (SELECT MAX(Salary) FROM Employees)
SELECT EmployeeId
FROM Employee AS eOuter
WHERE Salary > (
SELECT AVG(Salary)
FROM Employee eInner
WHERE eInner.DepartmentId = eOuter.DepartmentId
)
SELECT *
SELECT *
FROM Employees AS e
LEFT JOIN Supervisors AS s ON s.EmployeeID=e.EmployeeID
WHERE s.EmployeeID is NULL
The above finds cities from the weather table whose daily temperature variation is greater than 20. The result is:
city temp_var
ST LOUIS 21
LOS ANGELES 31
LOS ANGELES 23
LOS ANGELES 31
LOS ANGELES 27
LOS ANGELES 28
LOS ANGELES 28
LOS ANGELES 32
Here: the subquery (SELECT avg(pop2000) FROM cities) is used to specify conditions in the WHERE clause. The re
is:
name pop2000
San Francisco 776733
ST LOUIS 348189
Kansas City 146866
-- Or
EXEC Northwind.getEmployee @LastName = N'Ackerman', @FirstName = N'Pilar';
GO
-- Or
EXECUTE Northwind.getEmployee @FirstName = N'Pilar', @LastName = N'Ackerman';
GO
AS
BEGIN
-- insert audit record to MyAudit table
INSERT INTO MyAudit(MyTableId, User)
(SELECT MyTableId, CURRENT_USER FROM inserted)
END
BEGIN TRY
BEGIN TRANSACTION
INSERT INTO Users(ID, Name, Age)
VALUES(1, 'Bob', 24)
A database table should not be considered as just another table; it has to follow a set of rules to be considered tru
relational. Academically it is referred to as a 'relation' to make the distinction.
1. Each value is atomic; the value in each field in each row must be a single value.
2. Each field contains values that are of the same data type.
3. Each field heading has a unique name.
4. Each row in the table must have at least one value that makes it unique amongst the other records in the
table.
5. The order of the rows and columns has no significance.
Such a query allows users to rapidly find database tables containing columns of interest, such as when attemptin
to relate data from 2 tables indirectly through a third table, without existing knowledge of which tables may conta
keys or other useful columns in common with the target tables.
Using T-SQL for this example, a database's information schema may be searched as follows:
SELECT *
FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME LIKE '%Institution%'
The result contains a list of matching columns, their tables' names, and other useful information.
VT stands for 'Virtual Table' and shows how various data is produced as the query is processed
1. FROM: A Cartesian product (cross join) is performed between the first two tables in the FROM clause, and as
a result, virtual table VT1 is generated.
2. ON: The ON filter is applied to VT1. Only rows for which the is TRUE are inserted to VT2.
3. OUTER (join): If an OUTER JOIN is specified (as opposed to a CROSS JOIN or an INNER JOIN), rows from the
preserved table or tables for which a match was not found are added to the rows from VT2 as outer rows,
generating VT3. If more than two tables appear in the FROM clause, steps 1 through 3 are applied repeated
between the result of the last join and the next table in the FROM clause until all tables are processed.
4. WHERE: The WHERE filter is applied to VT3. Only rows for which the is TRUE are inserted to VT4.
5. GROUP BY: The rows from VT4 are arranged in groups based on the column list specified in the GROUP BY
clause. VT5 is generated.
6. CUBE | ROLLUP: Supergroups (groups of groups) are added to the rows from VT5, generating VT6.
7. HAVING: The HAVING filter is applied to VT6. Only groups for which the is TRUE are inserted to VT7.
10. ORDER BY: The rows from VT9 are sorted according to the column list specified in the ORDER BY clause. A
cursor is generated (VC10).
11. TOP: The specified number or percentage of rows is selected from the beginning of VC10. Table VT11 is
generated and returned to the caller. LIMIT has the same functionality as TOP in some SQL dialects such as
Postgres and Netezza.
Names should describe what is stored in their object. This implies that column names usually should be singular.
Whether table names should use singular or plural is a heavily discussed question, but in practice, it is more
common to use plural table names.
Keywords
SQL keywords are not case sensitive. However, it is common practice to write them in upper case.
At the minimum, put every clause into a new line, and split lines if they would become too long otherwise:
SELECT d.Name,
COUNT(*) AS Employees
FROM Departments AS d
JOIN Employees AS e ON d.ID = e.DepartmentID
WHERE d.Name != 'HR'
HAVING COUNT(*) > 10
ORDER BY COUNT(*) DESC;
Sometimes, everything after the SQL keyword introducing a clause is indented to the same column:
SELECT d.Name,
COUNT(*) AS Employees
FROM Departments AS d
JOIN Employees AS e ON d.ID = e.DepartmentID
WHERE d.Name != 'HR'
HAVING COUNT(*) > 10
(This can also be done while aligning the SQL keywords right.)
SELECT
d.Name,
COUNT(*) AS Employees
FROM
Departments AS d
JOIN
Employees AS e
ON d.ID = e.DepartmentID
WHERE
d.Name != 'HR'
HAVING
COUNT(*) > 10
ORDER BY
COUNT(*) DESC;
SELECT Model,
EmployeeID
FROM Cars
WHERE CustomerID = 42
AND Status = 'READY';
Using multiple lines makes it harder to embed SQL commands into other programming languages. However, man
languages have a mechanism for multi-line strings, e.g.,
@"..." in C#,"""...""" in Python, orR"(...)" in C++.
When usingSELECT * , the data returned by a query can change whenever the table definition changes. This
increases the risk that different versions of your application or your database are incompatible with each other.
Furthermore, reading more columns than necessary can increase the amount of disk and network I/O.
So you should always explicitly specify the column(s) you actually want to retrieve:
--SELECT * don't
SELECT ID, FName, LName, PhoneNumber -- do
FROM Emplopees;
However,SELECT * does not hurt in the subquery of an EXISTS operator, because EXISTS ignores the actual data
anyway (it checks only if at least one row has been found). For the same reason, it is not meaningful to list any
specific column(s) for EXISTS, SELECT
so * actually makes more sense:
The join condition is somewhere in the WHERE clause, mixed up with any other filter conditions. This makes
it harder to see which tables are joined, and how.
Due to the above, there is a higher risk of mistakes, and it is more likely that they are found later.
In standard SQL, explicit joins are the only way to use outer joins:
SELECT d.Name,
e.Fname || e.LName AS EmpName
FROM Departments AS d
LEFT JOIN Employees AS e ON d.ID = e.DepartmentID;
SELECT RecipeID,
Recipes.Name,
COUNT(*) AS NumberOfIngredients
FROM Recipes
LEFT JOIN Ingredients USING (RecipeID);
(This requires that both tables use the same column name.
USING automatically removes the duplicate column from the result, e.g., the join in this query returns a
singleRecipeID column.)
https://somepage.com/ajax/login.ashx?username=admin&password=123
strUserName = getHttpsRequestParameterString("username");
strPassword = getHttpsRequestParameterString("password");
and query your database to determine whether a user with that password exists.
txtSQL = "SELECT * FROM Users WHERE username = '" + strUserName + "' AND password = '"+ strPassword
+"'";
This will work if the username and password do not contain a quote.
However, if one of the parameters does contain a quote, the SQL that gets sent to the database will look like this:
-- strUserName = "d'Alambert";
txtSQL = "SELECT * FROM Users WHERE username = 'd'Alambert' AND password = '123'";
You could correct this by escaping quotes in username and password, e.g.:
cmd.CommandText = "SELECT * FROM Users WHERE username = @username AND password = @password";
cmd.Parameters.Add("@username", strUserName);
cmd.Parameters.Add("@password", strPassword);
If you do not use parameters, and forget to replace quote in even one of the values, then a malicious user (aka
hacker) can use this to execute SQL commands on your database.
Unfortunately for you, this is valid SQL, and the DB will execute this!
There are many other things a malicious user could do, such as stealing every user's email address, steal everyon
password, steal credit card numbers, steal any amount of data in your database, etc.
SQL = "SELECT * FROM Users WHERE username = '" + user + "' AND password ='" + pw + "'";
db.execute(SQL);
SELECT * FROM Users WHERE username = 'somebody' AND password ='pw' or '1'='1'
This one will pass the password check for all rows in Users
the table because'1'='1' is always true.