A CTE can be thought of as
a temporary result set and are similar to a derived table in that it is not
stored as an object and lasts only for the duration of the query. A CTE is
generally considered to be more readable than a derived table and does not
require the extra effort of declaring a Temp Table while providing the same
benefits to the user. However; a CTE is more powerful than a derived table as
it can also be self-referencing, or even referenced multiple times in the same
query.
The basic syntax structure
for a CTE is shown below: WITH MyCTE AS ( SELECT EmpID, FirstName, LastName, ManagerID FROM Employee WHERE ManagerID IS NULL ) SELECT * FROM MyCTE
Building a Recursive CTE
In the following examples,
you will learn how to harness the power of a recursive CTE query by fulfilling
a common business requirement, retrieving hierarchical data. By the time the
final query is complete you will be able to easily determine how many levels
from the top executive each employee is.
A recursive CTE requires
four elements in order to work properly.
1.Anchor query (runs once and the results ‘seed’
the Recursive query)
2.Recursive query (runs multiple times and is
the criteria for the remaining results)
3.UNION ALL statement to bind the Anchor and
Recursive queries together.
4.INNER JOIN statement to bind the Recursive
query to the results of the CTE.
WITH MyCTE AS ( SELECT EmpID, FirstName, LastName, ManagerID FROM Employee WHERE ManagerID IS NULL UNION ALL SELECT EmpID, FirstName, LastName, ManagerID FROM Employee INNER JOIN MyCTE ON Employee.ManagerID = MyCTE.EmpID WHERE Employee.ManagerID IS NOT NULL ) SELECT * FROM MyCTE
Identify the Anchor and Recursive
Query
Anyone who does not have a
boss is considered to be at the top level of the company and everyone who does
have a boss either works for the person(s) at the top level (upper management),
or the people that work for them (mid-management thru base employees).
For example, a CEO is at
the top level and thus has a ManagerID of null. Likewise, everyone below the
CEO will have a ManagerID. This is demonstrated in the two queries below:
The first SELECT statement
will become your Anchor query as it will find the employee that has a ManagerID
of null (representing Level 1 of the organization). The second SELECT statement
will become your Recursive query and it will find all employees that do have a
ManagerID (representing Level 2-3 of this organization).
As you can see from the
results so far, these queries are unable to give hierarchical data on which
level each employee is at within the organization.
Add the Anchor and Recursive query
to a CTE
Begin transforming this
entire query into a CTE by placing a UNION ALL statement between the Anchor and
Recursive queries. Now add parentheses around the entire query, indenting it,
moving it down, and adding the declaration WITH EmployeeList AS before the open
parenthesis, and then add SELECT * FROM EmployeeList on the next line after the
close parenthesis.
Your query should now look
like the screenshot below:
As you can see, the results
from your CTE are exactly the same as the results returned from running the
anchor and Recursive queries simultaneously in the previous example.
Add an expression to track
hierarchical level
The Anchor query (aliased
as ‘Boss’) inside the CTE represents everyone at Level 1 (i.e. Sally Smith).
The Recursive query (aliased as ‘Emp’) represents everyone at Levels 2 and 3.
In order to visualize each level in a result set, you will need to add an
expression field to each query.
Add the expression “1 AS
EmpLevel” to the Anchor query and the expression “2 AS EmpLevel” to the
Recursive query. Before executing the entire query, look closely at the
expression field. The EmpLevel expressions in the Anchor query will hard-code
the numeral 1 (for Sally Smith’s level), while the EmpLevel expressions in the
Recursive query will hard-code the numeral 2 for everyone else.
Your query should now look
like the screenshot below:
The two new expression
fields were a helpful step. In fact, they show the correct EmpLevel information
for Sally Smith and for the people at Level 2 (i.e., Adams, Bender, Brown,
Kennson, Lonning and Osako). However, the 2 is just a hard-coded placeholder to
help visualize your next step. Lisa Kendall and several other employees need to
be at Level 3.
Ideally you would like to
make the expression dynamic by replacing “2 AS EmpLevel” with the expression
“EmpLevel + 1”.
Add a self-referencing INNER JOIN
statement
Let’s take a moment and
recognise why this is not going to work quite so simply. The idea to increment
EmpLevel in the recursive query of the CTE is on the right track.
Unfortunately, the recursive query is trying to reference a field called
EmpLevel but can’t find one, since it has only been materialized in the result
set of the Anchor query and does not yet exist in the recursive set.
How can you materialize the
EmpLevel field for the recursive query? We
can use the CTE for this! Remember, a recursive CTE requires an
INNER JOIN to connect the recursive query to the CTE itself. Go ahead and write
an INNER JOIN statement binding the recursive query ‘Emp’ to the CTE
‘EmployeeList AS EL’ ON Emp.ManagerID = EL.EmpID.
Your query should now look
like the screenshot below:
Success!
You can now see that Sally is at the first level, Alex is at the second level
and Lisa appears at the third level. Since a CTE can reference itself, the
‘Emp’ recursive query can now access the EmpLevel field materialized in the
EmployeeList CTE.
Replication is intended to be a way of distributing
data automatically from a source database to one or more recipient
databases. As such, it can have obvious uses in a distributed system. It
has also been used to implement high-availability systems. It is not
useful for one-off synchronization, or for simply copying data. It is
intended as a long-term data relationship between databases. Typical
uses are in:
Data warehousing and reporting
Integrating data from several, possibly only partially connected, sites
Improving scalability and availability
Integrating heterogeneous data from other databases via OLE DB,
integrating data from mobile users, getting data to and from
Point-Of-Sale systems
Offloading/delegating batch processing tasks.
Examples of the use of replication within an application could be
Distributing data 'owned' by a particular application to other
applications that are 'consumers' of that data. (For example, sales
records to reporting services, manufacturing stock levels to purchasing
systems.)
Creating several instances of a database to distribute load on it
Updating a central database with information from a number of
remote Laptops that might be only partially connected, and to
resynchronise the laptops.
There are three methods of replication: Snapshot, Transactional, and
Merge. These are provided to try to meet the wide range of business
requirements for replication.
Replication uses a 'Magazine Publishing' vocabulary. Anything from an
'Article' to an entire database can be replicated. An Article is the
smallest component of distribution, and can be a table, procedure or
function. If it is a table, then a filter can be applied to it so that
only certain rows or columns are replicated. This 'Magazine Publishing'
analogy can be confusing because, in replication, a Subscriber can
sometimes make updates, and a Publisher usually sends out incremental
changes to the articles in a publication.
A 'Publisher' maintains the original copy of the data. It holds the
definition of the 'Publication', which defines the 'articles' that are
to be 'published'. (The database with the original location of the data
determines what is to be distributed).
A 'Subscriber' receives the articles from a publisher. It can
subscribe to one or more publications. Any database can take on either
role or even both roles at once.
A Distributor is a specialist database that runs the 'Replication agents'.
Replication is not part of the SQL Server engine, but an external
application. This makes it much easier to involve other database systems
in replication. Any SQL Server database, or other database system with
an OLE DB provider, can be a publisher or subscriber in snapshot or
transactional replication.
It is essential to plan out the replication in detail as a first
stage, and to be very certain of the type of replication you wish to
implement. A common mistake is to use replication in cases where a much
less complex solution is possible.
A problem with production systems using replication is the difficulty
of restoring the topology after a disaster.. This requires a fully
documented recovery strategy, which has to be periodically tested and
practiced. This means that the whole replication topology and
configuration must be scripted so it can be re-created in an emergency,
even if the system was originally built using the GUI tools. The Object
Browser (or Enterprise Manager) makes the initial deployment relatively
simple to do, and there are plenty of step-by-step guides, but it is not
the best option when trying to restore an existing topology, and
settings, in order to achieve a recovery from major failures.
Problems often follow from developers adding or dropping articles,
changing publication properties, and changing schema on published
databases. It is therefore best to create the replication once the
design of the publisher is relatively stable.
Replication topologies
In most topologies, it makes sense for publishers, distributors, and subscribers to be on separate physical hardware.
Central Publisher
The commonest form of replication is to have a single
publisher with the source data, with one or more subscribers. The
Distributor database can be on the same, or preferably different,
server.
Central Subscriber
Often, where the data from several databases need to
be 'warehoused' centrally in an OLAP or reporting database, one will
find a single 'reporting' database subscribing to several publications.
One can come across other topologies such as
'bi-directional' and 'peer to peer' which are really special cases of
Central Publisher or Central Subscriber and use transactional
replication
Publishing Subscriber
The distribution of data can be relayed to other
subscribers via a publishing subscriber. This allows replication to be
implemented over low-bandwidth WANs to a subscriber that, in turn,
distributes it to other servers within its high-bandwidth LAN
Replication Methods
How Replication Works
Replication begins with the initial synchronization of the published
objects between the Publisher and Subscribers, using a snapshot. A
snapshot is a copy of all of the objects and data specified by a
publication. After the snapshot is created on the publisher, it is
delivered to the Subscribers via the distributor.
For Snapshot replication, this is sufficient. For other types of
replication, all subsequent data changes to the publication flow to the
Subscriber as they happen, in a queue, or on request.
Snapshot Replication
The snapshot replication process provides the initial
synchronization for transactional and merge publications. However, in
several cases, this initial synchronization is all that is necessary.
This would include circumstances where data hardly changes, or if the
latest version of the data is not essential to the subscriber, where the
amount of data is small, or if a large number of changes takes place
rapidly.
Snapshot replication involves copying the articles
that make up the publication. Normally, if they exist already on the
subscriber, they are over-written, though this behavior can be changed.
Snapshot replication is more expensive in terms of overhead and network
traffic and only takes place at intervals. Because locks are held during
snapshot replication, this can impact other users of the subscriber
database. It is therefore more suitable for static data and
enumerations. In SQL Server 2005, several articles can be processed in
parallel, and interrupted snapshots can be recommenced from the point of
interruption. Snapshots can be queued or immediate.
Data changes are not tracked for snapshot
replication; each time a snapshot is applied, it completely overwrites
the existing data.
Transactional Replication
Transactional replication is used if:
Changes to the data must be propagated immediately
The database application taking out a subscription needs to react to every change
The Publisher has a very high volume of insert, update, and delete activity
The Publisher or Subscriber is a different database application reached via OLE DB.
Essentially, Transaction replication distributes data
in one direction, but transactional replication does offer options that
allow updates at the Subscriber. Once a snapshot replication has
synchronized the subscribers with the publisher, all committed
transactions on the publisher are then propagated to the subscribers in
sequence, via distributed transactions. One can select a queued update
or immediate, depending on requirements.
Peer-to-peer Replication
This is a special type of transactional replication
in which every participant is both a publisher and subscriber (2005
Enterprise only) and is most useful for up to ten databases in a
load-balancing or high-availability group.
Bidirectional Replication
This is where two databases replicate the same
articles to each other via a distributor. There must be loopback
detection. Data conflicts aren't handled and the replication must be
implemented in code, since the GUI doesn't support it.
Transactional replication tracks changes through the SQL Server transaction log
Merge Replication
Merge replication allows various sites to work autonomously and later merge updates into a single, uniform result.
Merge Replication is complex, but provides the means
to implement part of a high-availability system, as well as its original
purpose of serving mobile and disconnected users. It is designed for
cases where the publishers are not in constant communication with the
subscribers. After the initial snapshot synchronization, subsequent
changes are tracked locally with triggers, and the databases are merged
when in contact, using a series of rules to resolve all possible
conflicts.
Merge replication is used when several Subscribers
might need to update the same data at various times and propagate those
changes back to the Publisher and thence to other Subscribers. It is
also required in applications that involve Subscribers receiving data,
making changes offline, and finally reconnecting with the publisher to
synchronize changes with the Publisher and other Subscribers.
To make this possible, each Subscriber requires a
different partition of data and there has to be a set of rules to
determine how every conflict that takes place in the update of the data
is detected and resolved. These conflicts occur when the data is merged
because there can be no 'locking' and so the same data may have been
updated by the Publisher and by more than one Subscriber.
Merge Replication does not use transactions. Merge
replication uses a set of conflict-resolution rules to deal with all the
problems that occur when two databases alter the same data in different
ways, before updating the subscribers with a 'consensus' version. It
normally works on a row-by-row basis but can group rows of related
information into a logical record. One can specify the order in which
'articles' are processed during synchronisation.
Merge replication tracks changes through triggers and metadata tables.
Replication Agents
Replication is done by several different agents, which
are separate applications each responsible for part of the process. The
replication agents should not be run under the SQL Server Agent account
in a production system. Instead, they need the minimal permissions
necessary to perform their function.
SQL Server Agent
This manages the overall replication process via SQL Server Agent jobs.
The Snapshot agent
Snapshot.exe executes on the
Distributor. It extracts the schema and data defined by the publication,
which is then sent to the subscriber via a 'snapshot folder'. It also
updates status information on the distribution database. . It is used in
all forms of replication
The Log Reader Agent
LogRead.exe is used in transactional
replication to extract relevant committed transactions from the
publisher's log, repackage them and send them to the distributor in the
correct sequence.
Distribution Agent
Distrib.exe takes the snapshots, and log entries from the agents we've described, and dispatches them to the subscribers.
Merge Agent
ReplMer.exe is used only in Merge
Replication to send a snapshot when the subscriber is initialized, and
also exchanges transactions between publisher and subscriber
Queue Reader Agent
QrDrSvc.exe is used to queue the updates in transactional or snapshot replication when queuing has been specified.
Monitoring Replication
Many problems associated with replication can be
avoided by .regular checks. The most obvious check is to make sure that
the data has been transferred as expected. Periodic checks with SQL
Compare and SQL Data Compare can be very useful in addition to the tools
that come with Replication. Additionally the replication processes and
jobs need to be checked to make sure they are working.
Checking throughput
The performance of replication must be regularly monitored, and performance-tuned as necessary.
The Replication Monitor is used to check on the
operational state of publications, and inspect the history, and errors.
Right-clicking the replication node in Object Explorer will gain access
to it.
One of the most important concerns is the time delay,
or latency, of transactions from the publications appearing in the
subscriber database. At times of high transaction throughput on the
publisher database, bottlenecks can occur. Whereas the stored procedure
sp_browseReplCmds on the distribution database can tell you how far
behind the synchronisation is at any particular time, one cannot
determine where the problems lies just from the data. Tracer tokens are
now used to measure the actual throughput of the replication
architecture at any particular time to help diagnose such bottlenecks.
Validating
There is always an element of doubt as to whether the
replication has entirely worked. There are stored procedures provided
to compare the 'articles' on the publisher and subscribers to make sure
they are the same.
The sp_publication_validation stored procedure
validates the data associated with each article by calling
sp_article_validation (after the articles associated with a publication
have been activated). The sp_article_validation stored procedure invokes
sp_table_validation stored procedure, which calculates the number of
lines and, optionally, the checksum of the published table. It is
considered good practice to perform a daily row-count and weekly
checksum. SQL Data Compare is ideal for mending a broken replication.
The Distribution Agent raises the '20574' system
message if validation fails, or the '20575' system message if it passes.
The Distribution Agent will replicate changes to a subscriber even if
the validation shows that the subscriber is out of synchronization. It
is a good policy to configure the Replication Alert on the '20574'
message so as to send E-Mail, Pager, or Network notification.
This validation approach will only work within
certain restrictions. For example, it will not work if certain filters
have been applied. They should be used with caution.
Changing the settings
It is best to use the default replication settings
unless there are clear performance gains to be made, or if the
application design forces the issue. However, one cannot assume that the
changes will be generally beneficial to the entire topology without
comprehensive testing.
Articles
Articles are the smallest unit of a publication. An
article can be a table, view, stored Procedure or function. Where an
article is based on a table or view, it can contain all the data or just
part of it. These filters of two types.: More common are the static
'WHERE' clauses, but filters can be used dynamically in Merge
Replication to publish different 'content' (rows) to different
'subscribers' (databases receiving data). These latter Filters are
called 'Dynamic' and can be simple Row Filters, or Join Filters, where
the selection of rows to publish is based on a join with other tables,
rather than a simple WHERE clause.
Normally, any alteration to an article that is a table is propagated
to all the subscribers. You can also opt to propagate schema objects
associated with the article such as indexes, constraints, triggers,
collation and extended properties.
Updating articles
In Merge replication, the subscriber can update the
article. This is, of course, a recipe for conflict, and these have to be
resolved automatically. When the Merge Agent comes across a row that
might have changed recently, it examines the history or 'lineage' of
each site's version of the row to see if there is a conflict. If so,
then the update that is finally used. Has to be based on either
A "first wins" resolution,
a user-specified priority scheme to determine the update to select,
a customised resolution, using COM and stored procedures.
The 'lineage' is a history of changes in a table row
in MSmerge_contents, which is maintained automatically when a user
updates a row. Each column contains one entry for each site that has
updated the row.
Conflicts to the data in the base table can occur
within a column or a row. Most usual are column-tracked articles this
means that, within any row, updates are only recognized as conflicts if
the same column is updated by more than one subscriber. Occasionally,
however, the business rules of the application may treat simultaneous
changes to the any column within the row as a conflict, in which case
row-level tracking is used.
Programming Replication Topologies
Replication agents and replication topologies can be
administered and monitored remotely via SQL Scripts or RMO scripts. The
task of building and maintaining replication topologies is made much
easier of all parts of the deployments are scripted, even if this is
done after the other methods such as wizards or RMO are used for the
initial operation.
There are other uses for a replication script. It will be required in
end-user application where, for example, such as s a pull subscription
is synchronized when the user clicks a button, or where a routine
administration task such as monitoring replication throughput is
performed from a custom console. It is also generally used for writing
customized business rules that are executed when a merge subscription is
synchronized.
One can use the Object Explorer in SSMS, or the Enterprise Manager in
earlier versions of SQL Server, to set up replication. BOL provide
worked examples. Alternatively, RMO can be used with VB or C# to script
the replication. Whatever system you use, it is a good idea to use the
Transact SQL script as the reference for the replication topology you
create
Ultimately, the functionality in a replication topology is provided
by system stored procedures. The easiest approach is to use Transact-SQL
script files to perform a logical sequence of replication tasks,
because it provides a permanent, repeatable, copy of the steps used to
deploy the replication topology that can, for example, be used to
configure more than one subscriber. It also provides a measure of
documentation and disaster-recovery. A script can be stored as a query
object in a SQL Server Management Studio project.
Replication scripts can be created by hand, by the script generation
of the replication wizards in SQL Server Management Studio, or by using
Replication Management Objects (RMOs) to programmatically generate the
script to create an RMO object.
When creating scripts to configure replication, it is best to use
Windows Authentication so as to avoid storing security credentials in
the script file. Otherwise you must secure the script file
When we are developing a 'data manipulation page', we almost always do the same thing: A 'table/grid' that is used to 'show records'
of a table in a database, a 'create new record' page/dialog to add a new record to the database, an 'edit record' page/dialog to edit a record,
and finally a way of 'deleting a record' in the database.
Also, using AJAX, we can create more fast and interactive pages. Especially, jQuery and jQueryUI are invaluable
libraries to perform manipulation in an HTML page and perform AJAX requests to the server.
Users no longer need to leave the 'list of records' page to
create/edit or delete a record. Also, the page never refreshes itself to
reflect a change in records. When the user
deletes a record in the table, we can delete the corresponding row
from the table without refreshing the whole page. When the user edits a
record and saves it, we can change
the corresponding values in the table, and so on... Also, we can do
some animations while deleting, creating, or updating records.
All of the subjects I mentioned above are known techniques and we
have all implemented them. But the problem is that, we are
developing/coding almost
the same page for every type of record. Surely, we can
copy/paste/modify it! But is it a solution or another mess? All we know
is, copy/paste
is not desirable for all kinds of programming, it is an evil!
What is jTable
jTable [1] is a jQuery plug-in that
addresses the problem mentioned above. It takes a list and properties
of fields of a record and does all the job! It has several features:
Automatically creates an HTML table and loads records from the server using AJAX.
Automatically creates a 'create new record' jQueryUI dialog form. When the user creates a record, it sends data to the server using AJAX and adds the same record to the table in the page.
Automatically creates an 'edit record' jQueryUI dialog form. When the user edits a record, it updates the server using AJAX and updates all the cells on the table in the page.
Allow the user to 'delete a record' by jQueryUI dialog based confirmation. When the user deletes a record, it deletes the record
from the server using AJAX and deletes the record from the table in the page.
Supports server side AJAX based paging and sorting.
Allows user to select rows from table.
Allows user to resize columns.
Supports unlimited level of master/child tables.
Shows animations for create/delete/edit operations on the table.
Exposes some events to enable validation with forms.
It can be localized easily.
Table, forms and other elements are styled in a well defined and commented CSS file.
It comes with four pre-defined color themes: blue, red, green and purple, for now.
It is browser/platform independent and works on all common browsers.
It is not dependent on any server-side technology such as ASP.NET MVC, and can be used with others.
It has direct support for ASP.NET Web Forms Page Methods.
Here I will show how to develop a data manipulation page with ASP.NET MVC 3 and jTable. This sample project is included in the download file.
Assume that we are manipulating a Person list that has a lot of
information: name, city, email, password, gender, birth date, an 'about'
text, and education.
Using the page
First, I will show the capabilities of jTable. Here is the list of people:
This table is automatically created by jTable.
(Don't worry about the style of the table. The HTML code of the table is
not styled and is a clean HTML table. You can edit the CSS files
easily.) Also, it shows only the desired fields. Edit/Delete images
(buttons) are completely optional, and they are also automatically added
to each row. Title is also optional
and the add new record link is changeable by the user with another
element in the page. While the records are being loaded, a 'loading...' animation is shown.
When the user clicks the add new record link, a jQueryUI dialog based form is opened:
This form is also completely automatically created based on the fields of the record! When you fill the form and save,
jTable serializes the form and performs an AJAX call to the server. If the server responds 'OK', it adds the record to the table with an animation:
In the animation above, the row is highlighted with green. It turns
back to normal color after a few seconds. This animation is just a CSS
class transition and can be changed in
the CSS file easily. So you can change the animation to whatever you
want. If the server returns error while adding the record, jTable
automatically shows an error dialog message and does
not add the record to the table.
If you click the edit image (button) in a row, jTable automatically creates an editing jQuery dialog form:
jTable automatically creates and fills the form with
the selected record's values. When the user saves the form, just like
creating
a new record, the record is saved to the server. If the AJAX call is a
success, the record values are updated in the table and an 'edited' animation is shown:
As I mentioned above when creating the record, same animation
mechanism does exist while updating an edited record in the table. An
edited row turns to normal style in a few seconds.
When the user clicks the delete image (button), jTable shows a confirmation dialog:
If the user clicks the delete button, the record is deleted from the server using an AJAX call.
If the operation succeeds, it is also removed from the table automatically with a deleting animation:
The deleting row is highlighted for a second and removed from the table.
Now we will see how to implement the page above in ASP.NET MVC 3.
Model
We have two classes here: Person (represents a record in the People database table) and City (represents a record in the Cities database table). A person lives in a city.
So the Person class has a CityId that is the ID of a city row in the Cities table.
The Person class is shown below:
publicclass Person
{
publicint PersonId { get; set; }
// Id of a City in Cities
[Required]
publicint CityId { get; set; }
[Required]
publicstring Name { get; set; }
[Required]
publicstring EmailAddress { get; set; }
[Required]
publicstring Password { get; set; }
// "M" for mail, "F" for female.
[Required]
publicstring Gender { get; set; }
[Required]
public DateTime BirthDate { get; set; }
publicstring About { get; set; }
// 0: Unselected, 1: Primary school,
// 2: High school 3: University
[Required]
publicint Education { get; set; }
//true: Active, false: Passive
[Required]
publicbool IsActive { get; set; }
[Required]
public DateTime RecordDate { get; set; }
public Person()
{
RecordDate = DateTime.Now;
Password = "123";
About = "";
}
}
The [Required] attributes are not related to jTable as you probably know. They are used by ASP.NET MVC and Entity framework for validation. City is a simple class. It is designed to show the combobox feature of jTable (as you've seen above).
jTable always uses the POST method while making AJAX
calls to the server and expects a JSON object.
URLs (Controller/Action names in ASP.NET MVC) can be arbitrary and
they are set while creating a jTable instance (we will see this soon).
Getting the list
You must supply an action to jTable to get a list of records:
[HttpPost]
public JsonResult PersonList()
{
try
{
List persons = _personRepository.GetAllPersons();
return Json(new { Result = "OK", Records = persons });
}
catch (Exception ex)
{
return Json(new { Result = "ERROR", Message = ex.Message });
}
}
All methods must return a JSON object. Result property must be "OK" if operation is successful.
If an error occurs, Message property will contain an error message to show to the user. If Result is "OK", the Records property will contain an array of records
to show in the table.
You could pass some parameters to the action that can be used to get records based on some filters. Also, you can paginate or sort the table. We will see this later.
Creating
Creating a record is optional (we will see soon). If you allow user to create a record, you must supply an action to jTable to create a new record:
[HttpPost]
public JsonResult CreatePerson(Person person)
{
try
{
if (!ModelState.IsValid)
{
return Json(new { Result = "ERROR",
Message = "Form is not valid! " +
"Please correct it and try again." });
}
var addedPerson = _personRepository.AddPerson(person);
return Json(new { Result = "OK", Record = addedPerson });
}
catch (Exception ex)
{
return Json(new { Result = "ERROR", Message = ex.Message });
}
}
CreatePerson method must return the newly created object as the Record
property. This is needed since newly inserted record will has a key
(PersonId in this sample) and automatically generated values (such as
RecordDate here).
Updating
Editing a record is optional (we will see soon). If you allow user to edit a record, you must supply an action to jTable to update a record:
jTable can automatically download and fill comboboxes from a URL. For instance, the City combobox in the Person create/edit form above uses this feature. In such cases, you must supply an action to get the option list:
[HttpPost]
public JsonResult GetCityOptions()
{
try
{
var cities = _personRepository.GetCities().Select(
c => new { DisplayText = c.CityName, Value = c.CityId });
return Json(new { Result = "OK", Options = cities });
}
catch (Exception ex)
{
return Json(new { Result = "ERROR", Message = ex.Message });
}
}
The returning JSON object must have the Options property. It is an array of objects and every object has two properties: DisplayText and Value.
View
C# codes above was not directly related to jTable and specific to the ASP.NET MVC implementation. View side is completely about jTable. When you download jTable,
you will have a folder structure as shown below:
jquery.jtable.jsfile is the main and only
JavaScript file which you must include in your project. Other files (CSS
and images files) are used for styling the table and forms.
We will come back to styling later.
First, we add the jtable_blue.css file (my favourite style :) to the HEAD section of the HTML document (Razor view in ASP.NET MVC3):
You can add red or purple style files instead of blue, or you can write your own style file. Then we must add the jquery.jtable.js script file to the page:
Note that jTable is dependent on jQuery and jQueryUI (included UI effects). So, you must add those scripts to your page before jTable. If you don't have these libraries, go to http://jqueryui.com/download to download jQueryUI (it includes jQuery).
Finally, we can create the jTable instance like this:
Yes, it's a long definition but that's all! jTable
does not need anything else to create tables, forms, and animations.
I'll explain all options in the Details section but I want to explain
some basics now.
As you can see, jTable just needs a div container as the only HTML tag. It gets options:
title: Title of the table.
actions: URLs of actions that are used to create/delete/update/list records.
fields: All fields of the record. A field entry has properties
that define the field.
Finally, the load method of jTable is used to get
records from the server (we will see this in detail). You can always
call this method to load/refresh table data from the server.
Paging
jTable allows you server side paging with AJAX. See a demo here. It looks like the sample below:
To enable paging, paging option must set to true. You can also set pageSize option (default value is 10).
[HttpPost]
public JsonResult PersonList(int jtStartIndex, int jtPageSize)
{
try
{
int personCount = _personRepository.GetPersonCount();
List persons = _personRepository.GetPersons(jtStartIndex, jtPageSize);
return Json(new { Result = "OK", Records = persons, TotalRecordCount = personCount });
}
catch (Exception ex)
{
return Json(new { Result = "ERROR", Message = ex.Message });
}
}
Sorting
jTable allows you server side sorting with AJAX. See a demo here. It looks like the sample below:
To enable sorting, sorting option must set to true. You can also set defaultSorting option. It can be a field name of a column of the table. For instance, if you want table sorted by Name by default, defaultSorting can be 'Name ASC' or 'Name DESC'.
$('#PersonTable').jtable({
//...
sorting: true, //Enable sorting
defaultSorting: 'Name ASC', //Sort by Name by default
actions: {
//...
},
fields: {
//...
}
});
If sorting is enabled, jTable sends a query string parameter to the server on listAction AJAX call:
jtSorting: A string represents requested sorting. It is built from sorting field name plus sorting direction. For instance, It can be 'Name ASC', 'BirtDate DESC', 'Age ASC'... etc.
An ASP.NET MVC action that is used for sorting is shown below:
[HttpPost]
public JsonResult PersonList(int jtStartIndex = 0, int jtPageSize = 0, string jtSorting = null)
{
try
{
int personCount = _personRepository.GetPersonCount();
List persons = _personRepository.GetPersons(jtStartIndex, jtPageSize, jtSorting);
return Json(new { Result = "OK", Records = persons, TotalRecordCount = personCount });
}
catch (Exception ex)
{
return Json(new { Result = "ERROR", Message = ex.Message });
}
}
Note that while sorting can be used with paging (as in this sample), it is completely independed from paging.
Selecting
jTable allows you client side selecting rows. See a demo here. It looks like the sample below:
To enable selecting, selecting option must set to true. You can set multiselect option to true to allow user to select multiple rows at once. You can set selectingCheckboxes option to true to show checkboxes as the sample above. Finally, you can set selectOnRowClick to false to prevent row selecting on clicking anywhere on the row (it's true as default).
To get list of selected rows, you can call selectedRows method of jTable anytime (see sample usage). Also, you can get notified when selection changed by selectionChanged event.
//Prepare jtable plugin
$('#PersonTable').jtable({
//...
selecting: true, //Enable selecting
multiselect: true, //Allow multiple selecting
selectingCheckboxes: true, //Show checkboxes on first column
//selectOnRowClick: false, //Enable this to only select using checkboxes
actions: {
//...
},
fields: {
//...
},
//Register to selectionChanged event
selectionChanged: function () {
//Get all selected rows
var $selectedRows = $('#PersonTable').jtable('selectedRows');
$('#SelectedRowList').empty();
if ($selectedRows.length > 0) {
//Show selected rows
$selectedRows.each(function () {
var record = $(this).data('record');
$('#SelectedRowList').append(
'PersonId: ' + record.PersonId +
'Name:' + record.Name
);
});
} else {
//No rows selected
$('#SelectedRowList').append('No row selected! Select rows to see here...');
}
}
});
In the sample above, we are registering to selectionChanged event. In the event handler, we are getting selected rows by selectedRows method. It returns a jQuery selection, so we can call any jQuery method on it. We can get the record by record data property. Then we can get fields of record as record.Name, record.PersonId... etc.
Master/Child tables
jTable supports unlimited level of child tables for a
table. Child tables can also have their children and so on... A child
table is releated in a row in master table. Look at the screen below:
Click here to see a live demo.
When you click phone icon at left of a row, a new table slides down
below the clicked row and you can manipulate phones of the selected
person. You can do everything just as a regular jTable. When you click
close button, child table slides up and closed.
To implement a child table, first we must understand custom
(computed) column support of jTable. Green phone icon on the figure
above is created in a custom column. To create a custom column, we use display option of jtable field definition as like below:
Phones is a field definition as Name or Gender columns. But Person record has not a field named Phones. So, we define a function that will create value of this field to show on the table. display
function is used for that. It is called by jTable for each row. It must
return a text, HTML code or jQuery object. In the sample above, I
created an image (green phone icon) as a jQuery object and returned it.
Then jTable showed this image in the row. personData argument (you can change name of the argument of course) has record property that can be used to get record values for current row. So, if you want to use Name of the person, you can get it using personData.record.Name.
So far so good. But, how to open child table when user clicks this image. jTable defines two methods: openChildTable and closeChildTable to control child tables. So, web can open a child table when user clicks the phone icon (in the display method above):
openChildTable has three parameters. First one is used to indicate the row which is used as master row of child table. Here, I got container tr element of the image, so it gives the current row. Second parameter is a regular jTable initialization options.
You can pass any option that can be passed to any jTable instance. So,
you can define a custom column and open a second level child. Third and
the last parameter is a callback method that is called by jTable after
child table successfully created and opened. Here, I loaded records
after child table is opened.
Look at the listAction. It's something like that: '/PagingPerson/PhoneList?PersonId=' + personData.record.PersonId.
Thus, we are getting phone numbers those are related to current person
(personData comes from display method, see codes above). Also, we need
PersonId in the server side while creating a new phone number for a
person. It's done with a hidden field (See StudentId definition). Given
default value is used for new records.
When you create a child table, jTable automatically closes it when
user clicks close icon of the table. You can change/override these
functionalities. See details to learn in deep.
ASP.NET Web Forms support
jTable has direct support for ASP.NET Web Forms Page Methods since jTable v1.4.1. While jTable is already platform independed, I have built an extension
to jTable to support Page Methods in most proper way. For instance, see
the code below. This page method is used to get student list as paged
and sorted. It's pretty much similar to it's MVC version.
[WebMethod(EnableSession = true)]
publicstaticobject StudentList(int jtStartIndex, int jtPageSize, string jtSorting)
{
try
{
//Get data from database
int studentCount = Repository.StudentRepository.GetStudentCount();
List students = Repository.StudentRepository.GetStudents(jtStartIndex, jtPageSize, jtSorting);
//Return result to jTable
returnnew { Result = "OK", Records = students, TotalRecordCount = studentCount };
}
catch (Exception ex)
{
returnnew { Result = "ERROR", Message = ex.Message };
}
}
For more information on ASP.NET Web Forms support, see the tutorial in jTable.org. Also, the download file in this article includes samples in ASP.NET Web Forms.
Details
Now I will explain detailed usage of jTable.
Methods
jTable defines fallowing methods:
load(postData, completeCallback)
Loads records from the server. All parameters are optional. If you
want to pass some parameters to the server, you can pass them in the postData
argument while calling the load method, like this:
You can get people who are living in city 2 and whose name is Halil like shown above. Surely, you must handle these parameters
in the server side. Also, you can pass a callback method as completeCallback, that is called when loading of data is successfully completed. reload(completeCallback)
Re-loads records from server with last postData. This method can be used to refresh table data from server since it does not change current page, sorting and uses last postData (passed on last load call). Also, you can pass a callback method as completeCallback, that is called when loading of data is successfully completed.
selectedRows()
Gets all selected rows as jQuery selection. See the sample above. deleteRows(rows)
Deletes given rows from server and table. rows parameter must be a jQuery selection. This method can be combined with selectedRows method. Thus, you can get selected rows and pass to deleteRows method to delete them. openChildTable(row, tableOptions, opened)
This method is used to create and open a child table for a data row (See Master/Child section above). rowargument is a data row on the table, tableOptions are standard jTable options that is used to initialize child table. opened is a callback that is called by jTable when the child table is shown (After opening animation is finished). closeChildTable(row, closed)
This method is used to close an open child table for a table row. row is a jQuery row object (tr element) on the table. closed is a callback function that is called when child table is closed. openChildRow(row)
This method is used to open child row for a table row. Child rows
generally used to show child tables. If you want to show child tables,
you don't need to use this method, use openChildTable method instead. If
you want to open a custom child row, use this method. It returns the
opened child row. Thus, you can fill it with a custom content. A child
row is related to a specific data row in the table (which is passed as row agrument). If the data row is removed from table, it's child is also automatically removed. closeChildRow(row)
This method is used to close an open child row for a table row. See
openChildRow method. (This method is internally used by jTable to close
child tables.) getChildRow(row)
This method is used to get child row (tr element) for a table row.
Thus, you can add content to the child row. See openChildRow method. isChildRowOpen(row)
This method returns true if child row is open for specified row. See openChildRow method. addRecord(options), removeRecord(options), updateRecord(options)
These methods are used to manipulate table data programmatically. Since they have a bit detailed usage, please see reference documentation for these methods.
Actions
There are four main actions that are used by jTable to perform AJAX requests to the server:
listAction: A URL address to get the list of records.
createAction: A URL address to submit a create new record form.
updateAction: A URL address to submit an edit record form.
deleteAction: A URL address to delete a record.
If you don't want to use an action, just don't define it. For
instance, if you don't want to allow user to delete a row, don't supply
a deleteAction URL. Thus, jTable will not put a delete button for the records.
Field options
Fields are the core of jTable. They define the shape and behavior of your page. A field can have these properties:
title: A string as header in the table and forms for this field.
width: Width of the column for this field in the table. Can be any CSS unit (15%, 120px.., so on).
key: A boolean value that indicates whether this field is the key field of the record. Every record must has one
and only one key field that is used as key on update and delete operations.
list: A boolean value that indicates whether this field is shown in the table.
create: A boolean value that indicates whether this field is shown in the create record form.
edit: A boolean value that indicates whether this field is shown in the edit record form.
options: If this field's value will be selected in an
option list (combobox as default, can be radio button list), you must
supply a source.
An option source can be one of these values:
URL string: A URL to download the option list for this field (as we've seen above).
object: Property names are values, property values are display texts (see sample jTable instance in the View section above).
array: An array of values. If values of options are same as display texts, you can use this type.
type: Type of the field. If field is a string or number, no need to set the type. Other types are:
password: Show a password textbox for this field on edit/create forms.
textarea: Shows a textarea for this field on edit/create forms.
date: A date (not including time). You can also set the displayFormat option.
radiobutton: If field is a value from option, it can be a combobox (default) or radio button list. If it is a radio button list,
set type as radiobutton. You must supply options as mentioned above.
checkbox: To show a checkbox while editing this
field. You must supply values option for checked/unchecked
states (see the sample page in the View section). By default, a
checkbox's text changes when user changes the state of the checkbox. If
you want to fix the text,
you can set the formText option for this field. By
default, when the user clicks the checkbox's text, the state of the
checkbox changes. If you do not want that,
you can set setOnTextClick to false.
hidden: A hidden field can be used hidden fields in edit and create forms. It is not shown on the table. You may want to use defaultValue
option with hidden types, thus given default value is automatically set
to the hidden field on creating form. See master/child demo for sample
usage.
displayFormat: If the type is date, you can set its format. See jQueryUI datepicker formats [2].
defaultValue: You can set a default value for a
field. It must be a valid value. For instance, if the field is an option
list, it must be one of these options.
listClass: A string value that can be set as the class of a cell (td tag) of this field in the table. This way you can style the fields in the table.
inputClass: A string value that can be set as the class of an input item for this field in create/edit forms. So you can style input
elements in the forms for this field. This can be useful when working with validation plug-ins (we will see soon).
sorting: Indicates that whether this column will be used to sort the table (If table is sortable).
columnResizable: A boolean value that indicates
whether this column can be resized by user. Table's columnResizable
option must be set to true (it's default) to use this option.
display: This option is a function that allows you
to define a fully custom column for table. jTable directly shows return
value of this function on the table. See the sample below:
This sample Test column returns a bold 'test' string for all rows.
You can return any text, html code or jQuery object that will be shown
on the table. This method is called for each row. You can get record of
the row using data.record. So, if your record has Name property, you can use data.record.Name property to get the Name.
display function can be used for many purposes such as creating
calculated columns, opening child tables for a row... etc. See demos for
detailed usage.
input: This option is a function that allows you to
define a fully custom input element for create and edit forms. jTable
directly shows return value of this function on the form.
Here, we return a simple text input for Name field of the record. If
this input is being created for edit form, you can get editing record
using data.record property. If this input is being created for create
form, it is undefined. You can also use data.value to get current value
of the field. This is default value (if defined) of field for create
form, current value of field for edit form.
While jTable automatically created appropriate input element for each
field, you can use input option to create custom input elements.
Other options
jTable defines some other options that determine the general shape and behavior of the table and forms:
title: A string that is shown on top of the table. This is optional, if you don't supply the title option, no title is displayed.
addRecordButton: A jQuery object that can be used
instead of the 'add new record' link. Thus you can set any element on
the page
to open the 'add new record' dialog. If you set this option, the
bottom panel of jTable is not shown (since it will be empty).
deleteConfirmation: This option can be a boolean value or a function.
If it is a boolean value, that indicates whether a confirmation dialog
is shown when user clicks delete button/icon for a record (default value
is true). If this option is a function, It can fully control the delete
confirmation process. It must be a function like that: deleteConfirmation: function(data) { ... }
data argument has some properties to control confirmation process:
row: A jQuery selection for deleting row element.
record: Actual deleting record. For example, you can get Name property of record asdata.record.Name.
cancel: You can set data.cancel to true to cancel delete process (default value is false).
cancelMessage: If you cancelled delete
process, you can show a message to user that explains cancellation
reason. If you don't want to show any message, just don't set it.
deleteConfirm: A boolean value indicates whether to show a delete confirmation message or not (default value is true).
deleteConfirmMessage: If confirmation enabled, you can set a custom confirmation message.
For example, if you want to show some additional information to user on delete confirmation, you can write a function like that:
deleteConfirmation: function(data) {
data.deleteConfirmMessage = 'Are you sure to delete person ' + data.record.Name + '?';
}
defaultDateFormat: Default format of a date field. See jQueryUI datepicker formats [2]. Default: 'yy-mm-dd'.
dialogShowEffect: jQueryUI effect to be used while opening a
jQueryUI dialog. Some options are 'blind', 'bounce', 'clip', 'drop',
'explode', 'fold', 'highlight', 'puff', 'pulsate', 'scale', 'shake',
'size', 'slide'... etc. See jQueryUI documentation for all options. Default value is 'fade'.
dialogHideEffect: jQueryUI effect to be used while opening a
jQueryUI dialog. Some options are 'blind', 'bounce', 'clip', 'drop',
'explode', 'fold', 'highlight', 'puff', 'pulsate', 'scale', 'shake',
'size', 'slide'... etc. See jQueryUI documentation for all options. Default value is 'fade'.
messages: All messages that are shown by jTable. You can localize these messages (see the Localization section).
paging: Indicates that whether jTable uses paging or not.
pageSize: If paging enabled, this value indicates number of rows in a page.
sorting: Indicates that whether jTable will sort the table or not.
defaultSorting: Default sorting of table. It can be
a field name of a column of the table. For instance, if you want table
sorted by Name by default, defaultSorting can be 'Name ASC' or 'Name DESC'.
selecting: Indicates that whether jTable allows user to select rows on the table.
multiselect: Indicates that whether jTable allows user to select multiple rows at once.
selectingCheckboxes: Indicates that whether jTable shows checkbox column for selecting.
selectOnRowClick: Indicates that whether jTable
allows user to select a row by clicking anywhere on the row. This can be
set as false to allow user selecting a row only by clicking to the
checkbox (see selectingCheckboxes option).
showCloseButton: Indicates that whether jTable will
show a close button/icon for the table. When user clicks the close
button, closeRequested event is raised. This option is used internally
by jTable to close child tables. Default value is false.
openChildAsAccordion: If this options is set to
true, jTable automatically closes other open child tables when a child
table is opened (accordion style). Default value is false.
animationsEnabled: Indicates that whether jTable shows animations when user creates, updates or deletes a row.
tableId: A string that is a unique identifier for this table. This value is used on saving/restoring user preferences. It's optional.
columnResizable: A boolean value indicates that whether jTable allows user to resize data columns by dragging.
saveUserPreferences: Indicates that whether jTable
saves/loads user preferences such as resized columns. Saving/restoring
preferences are based on a hashed value that is generated using tableId,
all column names and total column count. So, changing columns will
change the hash and user preferences are reset.
Events
jTable defines some events when certain conditions occur.
formCreated(event, data): This event is raised when an edit or create form is created for a record. You can reach the form using the data.form argument. You can get
the type of the form using the data.formType argument. It can be 'edit' or 'create'. If formType is edit, you can reach the editing record
using the data.record argument (for example, you can get the name of the editing person as data.record.Name).
formSubmitting(event, data): This event is raised when save/submit button is clicked for a form. You can reach the form
using the data.form argument. You can get the type of the form using the data.formType
argument. It can be 'edit'
or 'create'. You can validate the form on this event. If you return
false from this event callback, the submit operation is cancelled.
formClosed(event, data): This event is raised when an edit/create form dialog is closed. You can reach the form using the data.form argument.
You can get the type of the form using the data.formType argument. It can be 'edit' or 'create'.
loadingRecords(event, data): This event is raised just before AJAX request to load records from server. It has no arguments.
recordAdded(event, data): This event is raised when user successfully creates and saves a new record. You can get the added record using data.record arguement. You can get the response JSON object returned from server as data.serverResponse. If jTable is not running on pagingmode, you can also get the added table row by data.row argument.
recordDeleted(event, data): This event is raised when user successfully deletes a record. You can get the deleted record using data.record argument. You can get the deleted table row by data.row argument. You can get the response JSON object returned from server as data.serverResponse.
recordsLoaded(event, data): This event is raised
when jTable loads records from server and refreshes the table (If paging
enabled, this event is also raised when user changes the current page).
You can get all records loaded from server by data.records argument. You can get the response JSON object returned from server as data.serverResponse.
recordUpdated(event, data): This event is raised when user successfully updates a record. You can get the updated record using data.record arguement. You can get the updated table row by data.row argument. You can get the response JSON object returned from server as data.serverResponse.
rowsRemoved(event, data): This event is
raised when either user deletes row/rows (actual record deletion from
server) or during re-loading records from server (all rows cleared but
not deleted from server). You can get all removed rows by data.rows as jQuery selection. You can get remove reason by data.reason (can be 'deleted' or 'reloading').
selectionChanged(event, data): This event is
raised when selected rows on the table changes in anyway. It may change
when user selects/deselects a row, a selected row is deleted, page
changed while some rows are selected... etc. You can get selected rows
by selectedRows method.
closeRequested(event, data): This event is
raised when user clicks close button/icon of the table. Close button is
shown if showCloseButton is set to true. This event has no argument.
rowInserted(event, data): This event is
raised when a row is inserted to the shown table. A new row can be
inserted either when user added a new record or records loaded from
server. When records loaded from server, rowInserted event is called for
each row. So, you can modify row or do whatever you need. You can get
the row using data.row, you can get related record with data.record. Finally, if this is a new record (that's added by user) data.isNewRow is set to true by jTable.
rowUpdated(event, data): This event is
raised when a row updated. A row is updated when user updates a record.
You can get the updated row using data.row, you can get related record with data.record. This event is raised after recordUpdated.
NOTE: formCreated, formSubmitting, and formClosed events are suitable to inject validation logic.
See the 'Combining with validation' section in this article.
Localization
jTable can be easily localized. You can use the messages option to localize a jTable instance on initialization. Default value of the messages option is shown below:
messages: {
serverCommunicationError: 'An error occured while communicating to the server.',
loadingMessage: 'Loading records...',
noDataAvailable: 'No data available!',
addNewRecord: '+ Add new record',
editRecord: 'Edit Record',
areYouSure: 'Are you sure?',
deleteConfirmation: 'This record will be deleted. Are you sure?',
save: 'Save',
saving: 'Saving',
cancel: 'Cancel',
deleteText: 'Delete',
deleting: 'Deleting',
error: 'Error',
close: 'Close',
cannotLoadOptionsFor: 'Can not load options for field {0}',
pagingInfo: 'Showing {0} to {1} of {2} records',
canNotDeletedRecords: 'Can not deleted {0} of {1} records!',
deleteProggress: 'Deleted {0} of {1} records, processing...'
}
If you want to use your own styles, you can start with the jtable_empty.css file. It defines all CSS selectors for jTable as empty. You can fill in the CSS selectors.
If you did like the jTable standard theme (defined in jtable_standard_base.css) but not happy with the colors, you can copy jtable_blue.css and change the colors.
Combining with validation
Validation is a common task while working with forms. jTable exposes some events (described above, in the Events section)
to inject validation logic to jTable auto-created forms. You can use your own validation logic or a jQuery validation plug-in.
Validation engine [3] is a powerful validation plug-in for jQuery. I definitely recommend it. Here I will show how to inject validation
engine to a jTable instance. First, see the validation engine in action:
As you can see, when I try to save the form, the validation engine
shows some error messages and prevent the submission of the form.
To be able to use the validation engine, first we must add style and JavaScript files to our HTML page: