Ignition Databases
Ignition Databases
Copyright 2011
All rights reserved
Inductive Automation
2110 21st St, Ste. 500
Sacramento, CA 95818
Table of Contents
Part I Databases
1 Ignition
...................................................................................................................................
Database Connections
5
2 What is
...................................................................................................................................
a Database?
6
3 SQL
................................................................................................................................... 8
13
16
22
27
35
37
Index
Databases
Part I
Databases
Databases
1.1
Databases
1.2
What is a Database?
A structured collection of records or data that is stored in a computer system.
Types of Databases
There are several common types of databases; each type of database has its own data model (how the
data is structured).
Flat Model
Hierarchical Model
Relational Model
Databases
What is a RDBMS?
A SQL Database System or Relational Database Management System (RDBMS) stores data in the form
of related tables, organizes and provides access to the data through a language called SQL.
Common Systems
MySQL
Microsoft SQL Server
Oracle
PostgreSQL
DB2
RDBMS Architecture
Databases
1.3
SQL
What is SQL?
SQL or Structured Query Language is a standard computer language for accessing and manipulating
database systems. SQL statements are used to create, maintain and query relational databases.
Examples:
SELECT
INSERT
UPDATE
DELETE
Tables
The foundation of every database system is a table. Every database consists of one or more tables,
which store the databases data / information. Each table is identified by a name (e.g. "Customers" or
"Orders") and consists of columns and rows.
The database table columns have their own unique names and have a pre-defined data types. Table
columns can have various attributes defining the column functionality (the column is a primary key, there
is an index defined on the column, the column has certain default value, etc.).
While table columns describe the data types, the table rows contain the actual data for the columns.
Primary Key
A primary key is a way to uniquely identify each row in a table. Every table must have a primary key. A
primary key comprises of a single column or set of columns.
No two distinct rows in a table can have the same value (or combination of values) in those columns.
Can be:
Auto-incrementing
Statically defined
Index
Indexes speed up the querying process by providing swift access to rows in the data tables, similarly to
Databases
the way a books index helps you find information quickly within that book.
Indexes are extremely important when querying large sets of data. You should create an index for each
set of columns you specify in a where clause. For example, you should add an index on the timestamp
column of a historical table when querying the table by a start and end date.
Foreign Key
A foreign key is a referential constraint between two tables. The foreign key identifies a column or a set
of columns in one (referencing) table that refers to a column or set of columns in another (referenced)
table.
The columns in the referencing table must be the primary key in the referenced table.
Example:
Supplier ( SupplierNumber, Name, Address, Type )
Invoices ( InvoiceNumber, SupplierNumber, Text )
NULL Value
NULL is a special marker used in SQL to indicate that a data value does not exist in the database.
SELECT Command
The SELECT statement is used to select data from a database. The result is stored in a result table,
called the result-set.
Examples:
SELECT * FROM Customers
SELECT Name FROM Customers
SELECT Name, Address FROM Customers
UPDATE Command
The UPDATE statement is used to update records in a table.
Examples:
UPDATE Customers SET Name = Inductive Automation
UPDATE Customers SET Address = 2110 21st Street WHERE ID = 1
INSERT Command
The INSERT statement is used to insert a new row in a table.
Example:
INSERT INTO Customers (Name, Address, City, State, Zip, Country, Phone)
VALUES (Inductive Automation, 2110 21st Street, Sacramento, CA,
95818, NULL, 800-266-7798)
INSERT INTO Customers SELECT * FROM OtherCustomers
DELETE Command
The DELETE statement is used to delete records in a table.
2012 Inductive Automation
Databases
10
Examples:
DELETE FROM Customers
DELETE FROM Customers WHERE Name = Inductive Automation
WHERE Clause
The WHERE clause is used to extract only those records that fulfill a specified criterion. Operators
Allowed in the WHERE Clause:
= Equal
<> Not equal
> Greater than
< Less than
>= Greater than or equal
<= Less than or equal
BETWEEN Between an inclusive range
LIKE Search for a pattern
IN If you know the exact value you want to return for at least one of the columns
The AND & OR operators are used to filter records based on more than one condition.
Examples:
SELECT * FROM Customers WHERE State = CA
SELECT Name FROM Customers WHERE Address LIKE %St% AND State = CA
ORDER BY Clause
The ORDER BY keyword is used to sort the result-set by a specified column. The ORDER BY keyword
sort the records in ascending order by default. If you want to sort the records in a descending order, you
can use the DESC keyword.
Examples:
SELECT * FROM Customers ORDER BY Name ASC
SELECT * FROM Customers ORDER BY State ASC, Name DESC
Joins
The JOIN keyword is used in an SQL statement to query data from two or more tables, based on a
relationship between certain columns in these tables. Tables in a database are often related to each
other with keys. A primary key is a column (or a combination of columns) with a unique value for each
row. Each primary key value must be unique within the table. The purpose is to bind data together,
across tables, without repeating all of the data in every table.
JOIN: Return rows when there is at least one match in both tables
LEFT JOIN: Return all rows from the left table, even if there are no matches in the right table
RIGHT JOIN: Return all rows from the right table, even if there are no matches in the left table
FULL JOIN: Return rows when there is a match in one of the tables
Example:
SELECT * FROM Contacts ct JOIN Customers cn ON cn.ID = ct.CustomerID
2012 Inductive Automation
Databases
11
GROUP BY Clause
The GROUP BY statement is used in conjunction with the aggregate functions to group the result-set by
one or more columns.
Example:
SELECT SUM(Duration) FROM Downtime GROUP BY CauseID
13
Datatype
INT
VARCHAR(45)
TEXT
VARCHAR(255)
FLOAT
PK
Y
N
N
N
N
NN
Y
Y
N
Y
Y
AI
Y
N
N
N
N
14
ControlLogix Processor
Processor in charge of the plant
Plant Floor
1
Once completed click on the green check mark to save. Press "Apply".
The table is now ready to use.
16
17
Eventually we will have a lot of rows of data. We need some way to filter the data. Let's add a search
text box above the table so the user can search for specific items. Drag in a Text Field component from
the Input tab of the component palette and place it above the table.
To make the table dynamic and use the search field we need to add to the SQL query. We are going to
make the query dynamic by adding a WHERE clause.
Click on the bind icon to the right of the data property and change the query to the following:
SELECT
*
FROM
inventory
WHERE
name LIKE '%{Root Container.Text Field.text}%'
ORDER BY
name ASC
18
Now when the user types into the text field the data in the table will change. Go into preview mode and
check it out.
19
You can see no row in the table has a name with the text "Test" inside of it. Let's make the table look
nicer. First, change the following properties:
Row Height
Background Mode
Odd Row Background
20
Alternating
255,255,204,255
Secondly, let's change the header names. Right click on the table and select Customizers > Table
Customizer. Here we can alter the way the table looks by customizing each column.
The user doesn't want to see the id column so check the "Hide" checkbox under the id column.
We want the quantity column to be centered so change the "Horiz Align" to "Center" under the quantity
column.
Lastly, change the "Header" name of each column:
name
description
location
quantity
Press "OK".
2012 Inductive Automation
Name
Description
Location
Qty
20
Almost there. Let's change the width of each column. Go into Preview mode and alter the widths of each
column to your liking. Once done leave preview mode and in order to save the widths you must go back
into the Table Customizer and press "OK". That makes the widths of each column stick.
Lastly, add a nice inventory label at the top and make the table fit the window. Remember to set the
layout mode correctly.
22
Empty String
Builtin/icons/24/add2.png
Add a new inventory item
In order to get the button to work correctly we need a new popup window. Let's now create the "Add
Inventory" popup window.
Create a new window by selecting File > New > Popup Window from the file menu. Call the window "Add
Inventory" and put it in the Popups folder.
The popup window will allow users to type in a name, description, location and quantity. It will have a
button to add the data and a button to cancel or close the window. We are going to create a simple form
using various input components such as a text field, numeric text field and a text area.
Drag in a Text Field component from the Input tab of the component palette. Name the component
"Name" by setting the Name property. Add a Label component to the left of the text field that says
"Name".
Do the same thing for the description, location and quantity using the following components:
Description
Location
Quantity
Text Area
Text Field
Numeric Text Field
Remember to name each component appropriately. I named the rest "Description", "Location" and
"Quantity".
Also change the Number Type property of the Quantity numeric text field to Float instead of Integer.
Next, add two buttons at the bottom of the window (from the Buttons tab of the palette) called "Add" and
"Cancel". You should have a window that looks like this:
23
All that is left is to configure the events on the add and cancel button. The cancel button is the easiest,
you just want it to close the window. Right click on the cancel button and select "Event Handlers...".
Select the actionPerformed event under action. On the right hand side choose the Navigation tab
and select the Close > This Window radio button.
24
Ok, now for the add button. Again right click on the button and select Event Handlers... Choose the
actionPerformed event under action. This time select the Script Editor tab on the right hand side and
enter in the following script and press "OK":
name = event.source.parent.getComponent('Name').text
desc = event.source.parent.getComponent('Description').text
loc = event.source.parent.getComponent('Location').text
qty = event.source.parent.getComponent('Quantity').floatValue
if name == "":
system.gui.messageBox("Please enter in a name")
elif qty <= 0:
system.gui.messageBox("Please enter in a quantity greater than 0")
else:
system.db.runPrepUpdate("INSERT INTO inventory (name, description,
location, quantity) VALUES (?,?,?,?)", [name, desc, loc, qty])
system.nav.closeParentWindow(event)
Notice the script validates the name and quantity fields making sure the name is not empty and the
quantity is greater than 0.
2012 Inductive Automation
25
That's it. The add window is done. Go back to the Inventory window. The add button now needs to open
this new popup window.
Right click on the add button and select Event Handlers... You will notice we are doing that a lot
(Control-J is faster). Select the actionPerformed event under action. Select the Navigation tab.
Choose the "Open" radio button and find the "Add Inventory" window.
27
Right click on the new window and rename it to "Edit Inventory". Again place it in the "Popups" folder.
Open the Edit Inventory window by double clicking on it. This window is going to edit one of the inventory
28
items in the database. We want to pre-fill the data in each input field. In order to do that we need to pass
in the id of the inventory item from the database (the value of the first column).
To pass parameters we need a custom property on the root container. Right click on the Root Container
and select Customizers > Custom Properties.
Press the green + icon to add a new Property. Name the property "id" and give it an Integer data type.
Press OK.
We have the id now and can use that to get the rest of the data from the table. Instead of binding each
input field to a SQL query resulting in 4 queries, we want to create a dataset to hold all of the data. It will
just be one row since we are only editing one inventory item at a time.
Right click on the Root Container and select Customizers > Custom Properties.
Press the green + icon to add a new Property. Name the property "data" and give it an Dataset data
type. Press OK.
Click on the bind icon to the right of the data property and select SQL Query. Type in the following SQL
query:
SELECT
*
FROM
inventory
WHERE
id = {Root Container.id}
The query references the id custom property on the root container. To add this property simply click on
the property link icon to the right and select the property.
Make sure the query is "Polling Off" so it only runs once. If you set the query to poll more than once it
will overwrite information that the user inputs.
29
Making progress. Now we need each input field to show the correct value from the dataset. Let's do the
name field first.
Select the name text field on the window. Bind the "Text" property of the component to an expression
binding. Enter in the following expression:
try({Root Container.data}["name"], "")
The expression goes inside of the dataset and gets out the name column's value. We didn't reference a
row because the first row is implied and we will only have one row of data.
Do the same thing for the rest of the components.
Description - "Text" property
try({Root Container.data}["description"], "")
Location - "Text" property
try({Root Container.data}["location"], "")
Quantity - "Value (Float)" property
try({Root Container.data}["quantity"], 0.0)
Notice the property and expression is a little bit different for the quantity field since it is a floating point
number. To test it out set the id column on the root container to 1. The components should get values.
30
All that is left is to alter the former add button to now be an edit button. Change the labels and buttons to
say edit inventory and save.
Right click on the Save button (add button previously) and select Event Handlers... There should already
be a script there replace it with the following:
id = event.source.parent.id
name = event.source.parent.getComponent('Name').text
desc = event.source.parent.getComponent('Description').text
loc = event.source.parent.getComponent('Location').text
qty = event.source.parent.getComponent('Quantity').floatValue
if name == "":
system.gui.messageBox("Please enter in a name")
elif qty <= 0:
31
The problem is that the value is the index into the dataset of the selected row. So if you select the first
row the value is 0. The second is 1 and so on. This is not the id value we need in order to open the edit
window. Don't worry there is a nice trick to get the id column. We will go inside of the table's data
dataset and get out the value of the id column for the selected row.
Right click on the Table and select Customizers > Custom Properties.
Press the green + icon to add a new Property. Name the property "selectedID" and give it an Integer
data type. Press OK.
Click on the bind icon to the right of the selectedID property and bind it to the following expression:
try({Root Container.Table.data}[{Root Container.Table.selectedRow},
"id"], -1)
32
The expression goes inside of the dataset getting out the value at the selectedRow row and the "id"
column. Since I have the first row selected the value should be 1 when I press "OK".
Now add an edit button to the right of the table just like the add button.
We only want the edit button enabled if the user has selected a row. So bind the "Enabled" property of
the edit button to the following expression:
{Root Container.Table.selectedID} != -1
We need the button to do something so right click on it and select Event Handlers... Again select the
actionPerformed event under action. Choose the Navigation tab.
Select the "Open" radio button and find the "Edit Inventory" window.
We need to pass in the selectedID value from the table. Check the "Pass Parameters" checkbox. Click
on the green + to add a new parameter. Name the parameter "id". Link the row to the selectedID
property on the table.
33
35
37
38
Check that this print statement is working right. Open the console again and test the button. You should
see 150 entries of the following.
<Row:3 columns>
You can access data in the rows using []. Change the print statement to in the for loop to the following
and check your results with the console. You should see the data from the table in the console when
you press the button.
for row in rawData:
print row[0], row[1], row[2]
Open the event handler again. Under the for loop, add the following code to test if the third column in the
dataset is over 75 or under 25. You can stop any line from running by putting a # in front of it. Comment
out the first print command in the for loop.
for row in rawData:
if row[2] >= 75:
level = high
elif row[2] <= 25:
level = low
else:
level =
print row[0], row[1], row[2], level
Open the console and test out your button. You should see something like this in the console for each
line (actual values will vary).
34 Test Row 6 83.2627920171505 high
To store this data in the newData array, use the python append() function on your new dataset. This line
should be tabbed in to the same level as your If statements.
newData.append([row[0], row[1], row[2], level])
Finally, convert the newData and header arrays to a dataset and store it in the tables data property.
Enter these lines of code and press the OK button.
finalData = system.dataset.toDataSet(header, newData)
event.source.parent.getComponent(Table).data = finalData
Click on your button and you should see the table component change from three to four columns with
the words high and low added to any column that is greater than 75 or less than 25.
The following is the full script with comments added for reference.
#get the table data (this is a comment using the # symbol)
table = event.source.parent.getComponent(Table)
rawData = system.dataset.toPyDataSet(table.data)
#create a new dataset
header = [col1, col2, col3, level]
2012 Inductive Automation
newData = []
#step through the old data
for row in rawData:
#test if the third column is too high or too low
if row[2] >= 75:
level = high
elif row[2] <= 25:
level = low
else:
level =
print row[0], row[1], row[2], level
#store the data in the new data set
newData.append([row[0], row[1], row[2], level])
#convert the new data into a dataset
finalData = system.dataset.toDataSet(header, newData)
event.source.parent.getComponent(Table).data = finalData
39