<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>SQL Archives | DMC, Inc.</title>
	<atom:link href="https://static.dmcinfo.com/blog/tag/sql/feed/index.xml" rel="self" type="application/rss+xml" />
	<link></link>
	<description></description>
	<lastBuildDate>Fri, 04 Sep 2026 15:35:12 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.2</generator>

<image>
	<url>https://static.dmcinfo.com/wp-content/uploads/2025/04/site-icon-150x150.png</url>
	<title>SQL Archives | DMC, Inc.</title>
	<link></link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>SQL Server Performance Tuning: When a Join Isn&#8217;t a Join</title>
		<link>https://static.dmcinfo.com/blog/47090/sql-server-join-performance-logical-vs-physical/</link>
		
		<dc:creator><![CDATA[Christopher Olsen]]></dc:creator>
		<pubDate>Wed, 15 Jul 2026 11:00:00 +0000</pubDate>
				<category><![CDATA[Digital Workplace Solutions]]></category>
		<category><![CDATA[Server Performance]]></category>
		<category><![CDATA[SQL]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/?p=47090</guid>

					<description><![CDATA[<p>Recently I was deploying an update to a client’s system and ran into a performance issue with the SQL script to apply database changes, an issue that hadn’t shown up in the test environment. I had to troubleshoot the slow query on the spot and solve it quickly. The cause of the problem turned out [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/47090/sql-server-join-performance-logical-vs-physical/">SQL Server Performance Tuning: When a Join Isn&#8217;t a Join</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph" id="h-recently-i-was-deploying-an-update-to-a-client-s-system-and-ran-into-a-performance-issue-with-the-sql-script-to-apply-database-changes-an-issue-that-hadn-t-shown-up-in-the-test-environment-i-had-to-troubleshoot-the-slow-query-on-the-spot-and-solved-it-quickly-the-cause-of-the-problem-turned-out-to-be-a-useful-demonstration-of-some-important-nuances-in-database-performance">Recently I was deploying an update to a client’s system and ran into a performance issue with the SQL script to apply database changes, an issue that hadn’t shown up in the test environment. I had to troubleshoot the slow query on the spot and solve it quickly. The cause of the problem turned out to be a useful demonstration of some important nuances in database performance.</p>



<p class="wp-block-paragraph">In this article, you’ll learn the difference between logical and physical joins, the factors SQL Server considers when deciding how to execute a join, and two approaches to resolving a slow SQL query.</p>



<h2 id="h-the-query" class="wp-block-heading">The Query</h2>



<p class="wp-block-paragraph">Here’s the SQL query that was running slowly. I’ve changed the table names and column names to obscure details about my client. After 5 minutes of running the query without finishing, I decided to abort it and troubleshoot it.</p>



<pre class="wp-block-preformatted">SELECT<br>&nbsp; &nbsp; v.CustomerID,<br>&nbsp; &nbsp; COUNT(v.ID) AS NumberOfVisits<br>FROM Customers AS c<br>INNER JOIN Visits AS v ON c.ID = v.CustomerID<br>WHERE v.VisitDate BETWEEN '2024-01-01' AND '2024-02-01'<br>GROUP BY v.CustomerID</pre>



<p class="wp-block-paragraph">It’s a simple query: it just joins customers to visits and selects the number of visits that each customer made within the date range specified.</p>



<p class="wp-block-paragraph">It’s important to note that the customers table is tiny, about 4,000 rows, and the visits table is large, several hundred <em>million </em>rows, so it’s understandable for this query to be performance-intensive. This exact query performed very fast in staging, but there was very little data for that date range in staging, so it makes sense for it to perform differently.</p>



<p class="wp-block-paragraph">What really confused me is that this query was a glorified copy-paste from a query that we had been using in production to run ad-hoc reports. When we ran it, it was dealing with production-scale data, so why did this version perform so poorly?</p>



<h2 id="h-the-execution-plan" class="wp-block-heading">The Execution Plan</h2>



<p class="wp-block-paragraph">The first step to troubleshooting a slow SQL query is to get an actual execution plan (if you can). This will show you exactly what steps SQL Server took to execute your query, and runtime stats such as how much time and CPU each step took, how many rows were returned by each step, etc.</p>



<p class="wp-block-paragraph">In SQL Server Management Studio (SSMS), press CTRL+M, then run your query. By default, this shows the execution plan only after the query finishes, which can be an issue for very slow queries. If you don’t want to wait, you can press CTRL + M <em>and</em> click the button near the top of SSMS labeled “Include Live Query Statistics”. This not only shows you the execution plan right away, but it also shows you the runtime stats (rows read, elapsed time of each step, etc.) in real time, so it updates as you watch it.</p>



<p class="wp-block-paragraph">I can’t share the screenshot of the execution plan from production, but here’s what it looked like in a development environment when I ran it later. Table and column names have been changed to obscure client details.</p>



<figure class="wp-block-image size-full is-resized has-custom-border"><img fetchpriority="high" decoding="async" width="1226" height="228" src="https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-1.png" alt="This image shows a SQL Server query execution plan, which is a graphical representation of how the database engine retrieves or modifies data." class="wp-image-47111" style="border-top-left-radius:20px;border-top-right-radius:20px;border-bottom-left-radius:20px;border-bottom-right-radius:20px;width:1200px;height:auto" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-1.png 1226w, https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-1-300x56.png 300w, https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-1-1024x190.png 1024w, https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-1-768x143.png 768w" sizes="(max-width: 1226px) 100vw, 1226px" /></figure>



<p class="wp-block-paragraph">I was profoundly surprised by this execution plan because there’s no physical join operator, despite the fact that there’s a logical join in the SQL query.</p>



<p class="wp-block-paragraph">When you go to a restaurant and order <em>steak au poivre</em>, you don’t give the waiter instructions to cook it (“crush some peppercorns, pat the steak dry, set the grill to medium-high…”). You just ask for steak au poivre, maybe specify medium-rare, and the chef makes it happen.</p>



<p class="wp-block-paragraph">SQL is the same way. SQL is a declarative language. When we write a SELECT query, we’re not writing instructions for <em>how</em> to get the data; we’re declaring <em>what data we want</em>. SQL Server needs to make decisions about how to execute it, such as which indexes to use or whether to go parallel.</p>



<p class="wp-block-paragraph">Similarly, when we write any kind of JOIN in a SQL query, what we’re writing is a <em>logical join. </em>Examples include INNER JOIN, LEFT OUTER JOIN, RIGHT OUTER JOIN, CROSS JOIN. SQL Server needs to figure out how to make your join happen, and it will do that by using one of several <em>physical join operators</em>. The most common physical join operators are Nested Loops, Hash Match, and Merge Join, so every execution plan you look at for queries involving logical joins will include at least one of those three operators.</p>



<h2 id="h-when-a-logical-join-does-not-require-a-physical-join" class="wp-block-heading">When a Logical Join Does Not Require a Physical Join</h2>



<p class="wp-block-paragraph">Our slow SQL query is an exception to that rule. Our query clearly has a logical join (INNER JOIN) between two tables, but the SQL Server chooses an execution plan with no physical joins.</p>



<p class="wp-block-paragraph">Why?</p>



<p class="wp-block-paragraph">The first thing that came to mind is that the SQL query, despite joining the Customers table and the Visits table, only includes fields from the Visits table in the SELECT clause, WHERE clause, and GROUP BY clause. We’re not using any fields from the Customers table other than its ID field as the join key.</p>



<p class="wp-block-paragraph">That’s part of the explanation, but it doesn’t explain all of it. The whole point of an INNER JOIN is to return only the records that have matches in both tables. So, even though we’re only SELECTing fields from one table, SQL Server still has to determine whether a given key value is present in <em>both</em> tables. How can it do that without performing a physical join?</p>



<p class="wp-block-paragraph">The answer is the foreign key reference that <em>enforces</em> the relationship between these two tables. Specifically, we have a foreign key on the Visits table on the CustomerID field, referencing the ID field of the Customers table. This foreign key enforces that no record can exist in the Visits table without a corresponding record in the Customers table.</p>



<p class="wp-block-paragraph">So, let’s look at this from SQL Server’s perspective:</p>



<ol class="wp-block-list">
<li>The user is asking me to perform an INNER JOIN.</li>



<li>He’s asking me to filter and group based on fields from only one of the tables.</li>



<li>He’s only asking for fields from that same table.</li>



<li>I know for a <em>fact</em> that all records in Visits have a corresponding Customer record.</li>



<li>Why the heck would I bother with a physical join? I’ll just read from the Visits table and ignore Customers.</li>
</ol>



<p class="wp-block-paragraph">Physical joins can be memory-intensive and time-consuming. Generally, if we can avoid a physical join, we’ll get our results faster. So, the execution plan makes sense.</p>



<p class="wp-block-paragraph">This isn’t just theory, either. I tested it in a development environment by removing the foreign key and running the SQL query. As expected, the execution plan included a physical join. Adding the foreign key constraint back to that field and running the query again resulted once more in an execution plan with no joins.</p>



<h2 id="h-why-is-the-query-slow-if-we-re-avoiding-a-join" class="wp-block-heading">Why Is the Query Slow If We&#8217;re Avoiding a Join?</h2>



<p class="wp-block-paragraph">Now that we’ve identified why the execution plan has no physical join, let’s circle back to the problem at hand: the query is slow. Why is it slow, and how do we fix it?</p>



<p class="wp-block-paragraph">Let’s take another look at the execution plan, specifically the first step.</p>



<figure class="wp-block-image size-full has-custom-border"><img decoding="async" width="325" height="197" src="https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-2.png" alt="This image shows an SQL Server execution plan indicating a NonClustered Index Scan on a table named &quot;Visits&quot;." class="wp-image-47543" style="border-top-left-radius:20px;border-top-right-radius:20px;border-bottom-left-radius:20px;border-bottom-right-radius:20px" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-2.png 325w, https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-2-300x182.png 300w" sizes="(max-width: 325px) 100vw, 325px" /></figure>



<p class="wp-block-paragraph">SQL Server is choosing to do an index scan. That means it starts at the very “top” of the index and reads through it to the end. It chose this index, “IX_Visits_CustomerID_VisitDate,” because it includes all the fields our query needs. This index contains a couple of fields of each row, which means SQL Server can fit more rows on a single page of data, allowing it to read more rows per page.</p>



<p class="wp-block-paragraph">However, the order of the keys in this index is hurting performance. CustomerID is the first key, and VisitDate is the second key. That means the rows in the index are sorted first by CustomerID,<em> then</em> by VisitDate. If it was the other way around, all the rows that match our WHERE clause would be right next to each other in the index and SQL Server would be able to do a “seek” to read only the minimal pages it needs to get the data we’re asking for.</p>



<h2 id="h-solution-1-add-a-new-index" class="wp-block-heading">Solution 1: Add a New Index</h2>



<p class="wp-block-paragraph">The most obvious solution is to add a new index with VisitDate as the first key and CustomerID as the second. Here’s the resulting execution plan:</p>



<figure class="wp-block-image size-full is-resized has-custom-border"><img decoding="async" width="820" height="125" src="https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-3.png" alt="SQL Server execution plan showing query processing steps, where a NonClustered Index Seek accounts for 97% of the query cost, with parallelism and hash aggregation used to improve performance and group results." class="wp-image-47131" style="border-top-left-radius:20px;border-top-right-radius:20px;border-bottom-left-radius:20px;border-bottom-right-radius:20px;aspect-ratio:6.560177671478603;width:925px;height:auto" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-3.png 820w, https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-3-300x46.png 300w, https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-3-768x117.png 768w" sizes="(max-width: 820px) 100vw, 820px" /></figure>



<p class="wp-block-paragraph">Even in this development environment, where the data is much smaller, this resulted in a 100x speed boost—the query finished in about 0.1 seconds instead of 10 seconds.</p>



<p class="wp-block-paragraph">However, indexes aren’t free, especially on large tables. The more indexes you have on a table, the longer it takes to write to that table when rows are updated or inserted. This table already has more indexes than I’d like, so I’d prefer to avoid adding a new one.</p>



<p class="wp-block-paragraph">Additionally, this query runs once a month as part of an automated report, so it feels silly to add a whole index just to make this one query faster.</p>



<p class="wp-block-paragraph">When we solve a slow query by adding, modifying, or removing indexes, that’s called “index tuning”.</p>



<h2 id="h-solution-2-change-the-query" class="wp-block-heading">Solution 2: Change the Query</h2>



<p class="wp-block-paragraph">In general, if you can make a query faster by rewriting the query rather than adding indexes, you should do so. I ran the old version of the query that was previously used to generate the ad-hoc reports and found that it used a physical join operator. The reason was simple: the original query joined the Visits table to a temp table representing Customers rather than the table itself. The Visits table obviously did not have a foreign key reference to this temp table, so it was forced to perform a physical join.</p>



<p class="wp-block-paragraph">For the new version of the query, I forced a physical join by selecting a field from the Customers table instead of the Visits table:</p>



<pre class="wp-block-preformatted">SELECT<br>&nbsp; &nbsp; c.ID AS CustomerID,<br>&nbsp; &nbsp; COUNT(v.ID) AS NumberOfVisits<br>FROM Customers AS c<br>INNER JOIN Visits AS v ON c.ID = v.CustomerID<br>WHERE v.VisitDate BETWEEN '2024-01-01' AND '2024-02-01'<br>GROUP BY c.ID</pre>



<p class="wp-block-paragraph">In this version of the query, I’m using the Customers table’s ID field for both the GROUP BY and one of the fields in the SELECT, while still using fields from the Visits table in the WHERE clause and the SELECT clause. This forces SQL Server to do a physical join, and the execution plan looks like this:</p>



<figure class="wp-block-image size-full is-resized has-custom-border"><img decoding="async" width="1330" height="314" src="https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-4.png" alt="SQL Server query execution plan showing a parallel execution strategy with Hash Match, Nested Loops, and Index Scan/Seek operators, where a Nested Loops join contributes a significant portion of the query cost." class="wp-image-47133" style="border-top-left-radius:20px;border-top-right-radius:20px;border-bottom-left-radius:20px;border-bottom-right-radius:20px;width:1200px;height:auto" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-4.png 1330w, https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-4-300x71.png 300w, https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-4-1024x242.png 1024w, https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-4-768x181.png 768w" sizes="(max-width: 1330px) 100vw, 1330px" /></figure>



<p class="wp-block-paragraph">In my development environment, the query takes 0.455. In production, it takes a couple of seconds, which is fast enough for our purposes. It runs as part of an automated report and must complete within 30 seconds to avoid timeouts.</p>



<p class="wp-block-paragraph">Let’s zoom in on that physical join operator:</p>



<figure class="wp-block-image size-full is-resized has-custom-border"><img decoding="async" width="598" height="299" src="https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-5.png" alt="SQL Server execution plan highlighting an Index Seek on the Visits table that accounts for 95% of the total query cost, with additional Nested Loops join and Clustered Index Scan operations processing a large volume of rows." class="wp-image-47134" style="border-top-left-radius:20px;border-top-right-radius:20px;border-bottom-left-radius:20px;border-bottom-right-radius:20px;width:552px;height:auto" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-5.png 598w, https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-5-300x150.png 300w" sizes="(max-width: 598px) 100vw, 598px" /></figure>



<p class="wp-block-paragraph">SQL Server chose the Nested Loops join. This physical join operator is very fast and efficient when you have a small result set on one side of the join compared to the result set on the other side. We have just a few thousand rows in our Customers table and a couple hundred million rows in our Visits table, so Nested Loops is the right choice.</p>



<p class="wp-block-paragraph">The way this join works is that SQL Server loops through the smaller data set, the Customers table, and for each row, performs a seek on the Visits table to get the rows corresponding to that CustomerID value. These seeks are fast because CustomerID is the first key in the index.</p>



<p class="wp-block-paragraph">Because this version of the query meets our performance needs without requiring a change to the database’s structure (e.g., by adding an index), it’s the solution we chose.</p>



<p class="wp-block-paragraph">When we solve a slow query by rewriting or tweaking the query text itself, as we did here, that’s called “query tuning”.</p>



<h2 id="h-the-takeaway" class="wp-block-heading">The Takeaway</h2>



<p class="wp-block-paragraph">What have we learned? We can take away the following lessons about SQL Server:</p>



<ol class="wp-block-list">
<li>SQL Server does not always need to perform a physical join to execute a logical join.</li>



<li>Foreign key references, or lack thereof, can impact whether SQL Server performs a physical join.</li>



<li>If you only use fields from one table in your SELECT, WHERE, ORDER BY, or GROUP BY clauses, SQL Server might not bother with a physical join.</li>



<li>When trying to speed up a slow query, you should first try to rewrite the query before resorting to adding or changing indexes.</li>



<li>Sometimes an unusual solution is the best one. In rare cases, adding a join can improve performance.</li>
</ol>



<p class="wp-block-paragraph">This was a deep dive into a fascinating detail of how SQL Server decides when to do a physical join and how that impacts performance.</p>



<p class="wp-block-paragraph" style="padding-bottom:var(--wp--preset--spacing--50)">If you rely on SQL Server or Azure SQL for your business-critical applications, you need database experts who can calmly work under pressure to solve tricky problems like the one described in this article.</p>



<div class="wp-block-group alignwide has-custom-light-blue-background-color has-background is-layout-flow wp-container-core-group-is-layout-dbd34961 wp-block-group-is-layout-flow" style="border-radius:20px;margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50);padding-top:var(--wp--preset--spacing--50);padding-right:0;padding-bottom:var(--wp--preset--spacing--50);padding-left:0">
<div class="wp-block-columns alignwide are-vertically-aligned-center is-layout-flex wp-container-core-columns-is-layout-43efaee5 wp-block-columns-is-layout-flex" style="padding-right:var(--wp--preset--spacing--60);padding-left:var(--wp--preset--spacing--60)">
<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:85%">
<h3 class="wp-block-heading has-text-align-left" id="h-have-an-upcoming-project-dmc-can-help-you-take-the-next-step">Have an upcoming project? DMC can help your team take the next step.</h3>



<p class="has-text-align-left wp-block-paragraph" id="h-need-help-turning-ideas-into-outcomes-automation-project-to-the-next-level-contact-us-today-to-learn-more-about-our-solutions-and-how-we-can-help-you-achieve-your-goals">Take your database performance to the next level with engineering solutions from DMC and learn more about <a href="https://static.dmcinfo.com/services/digital-workplace-solutions/">Digital Workspace Solutions</a>.</p>
</div>



<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:15%">
<div class="wp-block-buttons is-horizontal is-content-justification-center is-layout-flex wp-container-core-buttons-is-layout-2236275c wp-block-buttons-is-layout-flex">
<div class="wp-block-button is-style-fill"><a class="wp-block-button__link has-base-contrast-color has-text-color has-link-color wp-element-button" href="https://static.dmcinfo.com/contact/">Contact Us</a></div>
</div>
</div>
</div>
</div>
<p>The post <a href="https://static.dmcinfo.com/blog/47090/sql-server-join-performance-logical-vs-physical/">SQL Server Performance Tuning: When a Join Isn&#8217;t a Join</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Getting Started with Database Server in Beckhoff TwinCAT 3</title>
		<link>https://static.dmcinfo.com/blog/16328/getting-started-with-database-server-in-beckhoff-twincat-3/</link>
		
		<dc:creator><![CDATA[Carter Silvey]]></dc:creator>
		<pubDate>Fri, 03 May 2024 09:41:19 +0000</pubDate>
				<category><![CDATA[Beckhoff PLC]]></category>
		<category><![CDATA[Manufacturing Automation & Intelligence]]></category>
		<category><![CDATA[PLC]]></category>
		<category><![CDATA[Beckhoff]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[TwinCAT3]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/blog/16328/getting-started-with-database-server-in-beckhoff-twincat-3/</guid>

					<description><![CDATA[<p>Table of contents Being able to store long term data is one of the most crucial parts of a manufacturing or testing environment. Having access to this data can allow a team to track long term trends of their systems. For instance, one team&#160;could track the uptimes of the various production lines to find where [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/16328/getting-started-with-database-server-in-beckhoff-twincat-3/">Getting Started with Database Server in Beckhoff TwinCAT 3</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 id="h-table-of-contents" class="wp-block-heading">Table of contents</h2>



<ol class="wp-block-list">
<li><a href="#download">Download and Installation</a></li>



<li><a href="#database">Adding the Database Project to a TwinCAT 3 Solutions</a></li>



<li><a href="#code">Writing Code to Write to the Database Server</a></li>



<li><a href="#testing">Testing the Database Connection</a></li>
</ol>



<p class="wp-block-paragraph">Being able to store long term data is one of the most crucial parts of a manufacturing or testing environment. Having access to this data can allow a team to track long term trends of their systems. For instance, one team&nbsp;could track the uptimes of the various production lines to find where the bottlenecks in the process flow are. Another team could use their motor&#8217;s&nbsp;speed data to track when it needs maintenance.</p>



<p class="wp-block-paragraph">Beckhoff TwinCAT 3 offers the ability to run a database server directly on the IPC using their TF6420 module. This module allows you to send data up to most types of databases, though this blog will provide specific examples by using a <a href="https://www.postgresql.org/" target="_blank">PostgreSQL</a> database since it is free to use,&nbsp;open source, and uses structured query language (SQL).</p>



<h2 id="download" class="wp-block-heading">1. Download and Installation</h2>



<h3 id="h-1-1-download" class="wp-block-heading">1.1. Download</h3>



<p class="wp-block-paragraph">First,&nbsp;<a href="https://www.beckhoff.com/en-us/products/automation/twincat/tfxxxx-twincat-3-functions/tf6xxx-connectivity/tf6420.html?" target="_blank" rel="noreferrer noopener">Download TF6420 from the Beckhoff website</a>. The installer is located in Documentation and Downloads&nbsp;&gt; Software and Tools.</p>



<figure class="wp-block-image aligncenter size-full"><img decoding="async" width="866" height="619" src="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-download.png" alt="" class="wp-image-34727" srcset="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-download.png 866w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-download-300x214.png 300w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-download-768x549.png 768w" sizes="(max-width: 866px) 100vw, 866px" /></figure>



<h3 id="h-1-2-installation" class="wp-block-heading">1.2. Installation</h3>



<p class="wp-block-paragraph">Once downloaded you will need to run the .EXE on both the engineering computer and on the PLC. To download onto the PLC, the simplest way is to move the .EXE onto a USB and plug this into the PLC. Once the USB is plugged in, you can either remote into the PLC (Remote Desktop Connection) or connect a monitor and mouse using any of the outputs on the PLC. Once connected, run the .EXE on the PLC and install the software.</p>



<h3 id="h-1-3-open-twincat-xae" class="wp-block-heading">1.3. Open TwinCAT XAE</h3>



<p class="wp-block-paragraph">Once the installer is complete, open the TwinCAT XAE solution in which the database connection should be installed. If you haven&#8217;t already installed TwinCAT XAE or configured your Beckhoff IPC yet, <a href="https://static.dmcinfo.com/blog/19695/getting-started-with-a-beckhoff-plc-part-one-setup/">check out our setup blog on how to get started </a>and then come back to step 2.</p>



<h2 id="database" class="wp-block-heading">2. Adding the Database Project to a TwinCAT 3 Solution</h2>



<h3 id="h-2-1-add-new-database-server-nbsp-project" class="wp-block-heading">2.1. Add New Database Server&nbsp;Project</h3>



<p class="wp-block-paragraph">Within the project, right-click on the project solution -&gt; Add -&gt; New Project</p>



<figure class="wp-block-image aligncenter size-full"><img decoding="async" width="866" height="438" src="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-add-new-project.png" alt="" class="wp-image-34731" srcset="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-add-new-project.png 866w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-add-new-project-300x152.png 300w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-add-new-project-768x388.png 768w" sizes="(max-width: 866px) 100vw, 866px" /></figure>



<p class="wp-block-paragraph">In the window that opens, select TwinCAT Connectivity and then choose &#8220;Empty TwinCAT Database Server Project&#8221;</p>



<figure class="wp-block-image aligncenter size-full"><img decoding="async" width="866" height="602" src="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-empty-twincat-database-server-project.png" alt="" class="wp-image-34733" srcset="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-empty-twincat-database-server-project.png 866w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-empty-twincat-database-server-project-300x209.png 300w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-empty-twincat-database-server-project-768x534.png 768w" sizes="(max-width: 866px) 100vw, 866px" /></figure>



<h3 class="wp-block-heading">2.2. Add and Configure the Database</h3>



<p class="wp-block-paragraph">Once the new project is added, there should be a default TcDbServer already added under the project. There is currently no database in the project, but this can be done by right-clicking TcDbServer -&gt; Add New Database</p>



<figure class="wp-block-image aligncenter size-full"><img decoding="async" width="433" height="576" src="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-add-new-database.png" alt="" class="wp-image-34734" srcset="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-add-new-database.png 433w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-add-new-database-226x300.png 226w" sizes="(max-width: 433px) 100vw, 433px" /></figure>



<p class="wp-block-paragraph">You will then see a new DB server appear under TcDbServer. The server can be configured by double-clicking the DB. This will bring up the configuration window. Currently the window is empty, but this can be fixed by selecting the database type pictured below. For this example, a PostgreSQL database will be used.</p>



<figure class="wp-block-image aligncenter size-full"><img decoding="async" width="866" height="436" src="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-postgresql.png" alt="" class="wp-image-34735" srcset="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-postgresql.png 866w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-postgresql-300x151.png 300w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-postgresql-768x387.png 768w" sizes="(max-width: 866px) 100vw, 866px" /></figure>



<p class="wp-block-paragraph">Once the database type is selected, the settings will appear and can be configured there. For PostgreSQL, the first few settings are Host, Database, and Port. The next option is Authentication which contains a drop down with different kinds of authentication. The example below is going to use Username/Password.</p>



<p class="wp-block-paragraph"><em>Note:&nbsp;The password used here is encrypted and is not stored in plain text anywhere on the PLC</em></p>



<figure class="wp-block-image aligncenter size-full"><img decoding="async" width="442" height="269" src="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-username-password.png" alt="" class="wp-image-34736" srcset="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-username-password.png 442w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-username-password-300x183.png 300w" sizes="(max-width: 442px) 100vw, 442px" /></figure>



<h3 id="h-2-3-set-target-ams-net-id" class="wp-block-heading">2.3. Set Target AMS Net ID</h3>



<p class="wp-block-paragraph">Once all the information is added, the connection is not quite ready to be tested. The AMS Net ID must be set up first. To do this, go back to TcDbServer and double click the tab. Once the server settings window is open, there is a dropdown menu in the top left where all the options for Ams Net Ids appear.</p>



<p class="wp-block-paragraph">For testing on a local machine select &lt;Local&gt;.</p>



<p class="wp-block-paragraph">If this is running on a PLC and you are connected to the PLC, the name of the PLC will be on this menu and you can choose that instead.</p>



<figure class="wp-block-image aligncenter size-full"><img decoding="async" width="866" height="451" src="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-ams-net-selection.png" alt="" class="wp-image-34739" srcset="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-ams-net-selection.png 866w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-ams-net-selection-300x156.png 300w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-ams-net-selection-768x400.png 768w" sizes="(max-width: 866px) 100vw, 866px" /></figure>



<h3 id="h-2-4-tf6420-license" class="wp-block-heading">2.4. TF6420 License</h3>



<p class="wp-block-paragraph">Before checking the connection to the server, the TF6420 license must be activated. For now, a trial license can be generated. First start by going to the Manage License tab at the top of the window that has opened. Next scroll all the way down to TF6420 TC3 Database Server and check the box next to it that says cpu license.</p>



<figure class="wp-block-image aligncenter size-full"><img decoding="async" width="866" height="296" src="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-license.png" alt="" class="wp-image-34741" srcset="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-license.png 866w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-license-300x103.png 300w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-license-768x263.png 768w" sizes="(max-width: 866px) 100vw, 866px" /></figure>



<p class="wp-block-paragraph">After this box is checked, go back to the main license screen and into the Order Information (Runtime) tab. Once here go down to the 7 Days Trial License and click on that. A small screen will then appear in which you will type a short captcha. Once this is typed, hit Ok and the license will be generated.</p>



<figure class="wp-block-image aligncenter size-full"><img decoding="async" width="866" height="432" src="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-7-day-trial-license.png" alt="" class="wp-image-34742" srcset="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-7-day-trial-license.png 866w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-7-day-trial-license-300x150.png 300w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-7-day-trial-license-768x383.png 768w" sizes="(max-width: 866px) 100vw, 866px" /></figure>



<h3 id="h-2-5-activate-database-configuration" class="wp-block-heading">2.5. Activate Database Configuration</h3>



<p class="wp-block-paragraph">Next, activate the configuration of the database connection by right-clicking the DB and selecting Activate Configuration.</p>



<figure class="wp-block-image aligncenter size-full"><img decoding="async" width="433" height="555" src="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-activate-configuration.png" alt="" class="wp-image-34744" srcset="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-activate-configuration.png 433w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-activate-configuration-234x300.png 234w" sizes="(max-width: 433px) 100vw, 433px" /></figure>



<p class="wp-block-paragraph">(Optional) The database server toolbar&nbsp;is optional, but will be beneficial moving forward. At the top of the screen on the task bar, right-click and select TwinCAT Database Server. This will add a new set of tools at the top that can be used with the databases (one of them being activate configuration).</p>



<figure class="wp-block-image aligncenter size-full"><img decoding="async" width="320" height="789" src="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-add-database-toolbar.png" alt="" class="wp-image-34745" srcset="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-add-database-toolbar.png 320w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-add-database-toolbar-122x300.png 122w" sizes="(max-width: 320px) 100vw, 320px" /></figure>



<p class="wp-block-paragraph">Here is what the toolbar will look like when added:</p>



<figure class="wp-block-image aligncenter size-full"><img decoding="async" width="320" height="28" src="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-database-toolbar.png" alt="" class="wp-image-34746" srcset="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-database-toolbar.png 320w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-database-toolbar-300x26.png 300w" sizes="(max-width: 320px) 100vw, 320px" /></figure>



<h3 id="h-2-6-check-database-connection" class="wp-block-heading">2.6. Check Database Connection</h3>



<p class="wp-block-paragraph">After activating configuration, check this connection by going back to the DB page. At the bottom, there will be 2 options, CREATE and CHECK. Select CHECK.</p>



<figure class="wp-block-image aligncenter size-full"><img decoding="async" width="866" height="687" src="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-check-database.png" alt="" class="wp-image-34747" srcset="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-check-database.png 866w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-check-database-300x238.png 300w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-check-database-768x609.png 768w" sizes="(max-width: 866px) 100vw, 866px" /></figure>



<p class="wp-block-paragraph">If everything went well, you should see a pop up that says the check succeeded. If not, verify all database information is correct and go back to step 8.</p>



<figure class="wp-block-image aligncenter size-full"><img decoding="async" width="314" height="196" src="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-configuration-check-succeeded.png" alt="" class="wp-image-34748" srcset="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-configuration-check-succeeded.png 314w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-configuration-check-succeeded-300x187.png 300w" sizes="(max-width: 314px) 100vw, 314px" /></figure>



<h3 id="h-2-7-add-database-to-configuration-pool" class="wp-block-heading">2.7. Add Database to Configuration Pool</h3>



<p class="wp-block-paragraph">Once the database is configured, it needs to be added to the DB configuration pool. This can be done by right-clicking on the DB and hitting the Add to DB Configuration Pool tab.</p>



<figure class="wp-block-image aligncenter size-full"><img decoding="async" width="433" height="482" src="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-add-to-configuration-pool.png" alt="" class="wp-image-34749" srcset="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-add-to-configuration-pool.png 433w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-add-to-configuration-pool-270x300.png 270w" sizes="(max-width: 433px) 100vw, 433px" /></figure>



<h2 id="code" class="wp-block-heading">3. Writing Code to Write to the Database Server</h2>



<h3 id="h-3-1-installing-reference-libraries" class="wp-block-heading">3.1. Installing Reference Libraries</h3>



<p class="wp-block-paragraph">Once added to the DB Configuration Pool, there are a few basic functions to test the connection and write a few queries. In order to use these, we will need to install 2 additional libraries. To do this, go to the PLC project that the database will be called in. Then go to the references tab in the project tree pictured below. Right-click this and then select Add library.</p>



<figure class="wp-block-image aligncenter size-full"><img decoding="async" width="433" height="428" src="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-add-library.png" alt="" class="wp-image-34750" srcset="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-add-library.png 433w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-add-library-300x297.png 300w" sizes="(max-width: 433px) 100vw, 433px" /></figure>



<p class="wp-block-paragraph">This will open a library window where the 2 libraries needed for database are located. Tc3_Database and Tc3_EventLogger.</p>



<figure class="wp-block-image aligncenter size-full"><img decoding="async" width="866" height="771" src="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-tc3-database-and-tc3-eventlogger.png" alt="" class="wp-image-34751" srcset="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-tc3-database-and-tc3-eventlogger.png 866w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-tc3-database-and-tc3-eventlogger-300x267.png 300w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-tc3-database-and-tc3-eventlogger-768x684.png 768w" sizes="(max-width: 866px) 100vw, 866px" /></figure>



<h3 id="h-3-2-example-code" class="wp-block-heading">3.2. Example Code</h3>



<p class="wp-block-paragraph">We can now use the built-in TwinCAT functions to send a basic SQL command. Again, for this example we&#8217;re using PostgreSQL functions and syntax. Under POUs, open MAIN and put the following code in the variable declaration section:</p>


<pre class="”brush:pascal”"><br /><br />
</pre>


<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">Pascal</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>VAR 
    nStep, nNextStep : INT; 
    sQuery : STRING; 
    fbDatabase : FB_SQLDatabaseEvt(sNetID := '', tTimeout := T#5S); 
    fbSqlCommand : FB_SQLCommandEvt(sNetID := '', tTimeout := T#5S); 
    tcMessage : I_TcMessage;  
END_VAR</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">VAR</span><span style="color: #D4D4D4"> </span></span>
<span class="line"><span style="color: #D4D4D4">    nStep, nNextStep : INT; </span></span>
<span class="line"><span style="color: #D4D4D4">    sQuery : </span><span style="color: #569CD6">STRING</span><span style="color: #D4D4D4">; </span></span>
<span class="line"><span style="color: #D4D4D4">    fbDatabase : FB_SQLDatabaseEvt(sNetID := </span><span style="color: #CE9178">&apos;&apos;</span><span style="color: #D4D4D4">, tTimeout := T</span><span style="color: #CE9178">#5</span><span style="color: #D4D4D4">S); </span></span>
<span class="line"><span style="color: #D4D4D4">    fbSqlCommand : FB_SQLCommandEvt(sNetID := </span><span style="color: #CE9178">&apos;&apos;</span><span style="color: #D4D4D4">, tTimeout := T</span><span style="color: #CE9178">#5</span><span style="color: #D4D4D4">S); </span></span>
<span class="line"><span style="color: #D4D4D4">    tcMessage : I_TcMessage;  </span></span>
<span class="line"><span style="color: #D4D4D4">END_VAR</span></span></code></pre></div>



<p class="wp-block-paragraph">Here is a&nbsp;brief explanation of each variable:</p>



<ul class="wp-block-list">
<li>nStep and nNextStep are used to drive a state machine.</li>



<li>sQuery is the raw string for the query we want to send to the database</li>



<li>FB_SQLDatabaseEvt is responsible for connecting, disconnecting and overall communication with the database.</li>



<li>FB_SQLCommandEvt is responsible for generating a SQL command to be sent via the FB_SQLDatabaseEvt. Note: A standard STRING will not work if being sent through FB_SQLDatabaseEvt, even if the syntax is correct.</li>



<li>I_TcMessage will store any messages sent back from the database server. This will be helpful for identifying errors if there are any.</li>
</ul>



<p class="wp-block-paragraph">In the code execution section of MAIN, add the following basic state machine:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">Pascal</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>//Generate the query string.
//Change TableName to your table's name.
//Change column1 and column2 to the column names (include any other columns your table has, separated by ,).
//Change $'value1$' and $'value2$' to the values you wish to send.
//Note: $ is an escape character for a single quote. Single quotes indicate a string.

sQuery := 'INSERT INTO TableName (column1, column2) values ($'value1$', $'value2$')';

CASE nStep OF
    0:
        //Connect to the database at database ID 1
        IF fbDatabase.Connect(1) THEN
            nNextStep := 10;
        END_IF
    10:
        //Move the database message result to tcMessage
        tcMessage := fbDatabase.ipTcResult;
        //Go to disconnect step if there's an error
        IF fbDatabase.bError THEN
            nNextStep := 99;
        END_IF
        //Create a SQL command
        IF fbDatabase.CreateCmd(ADR(fbSqlCommand)) THEN
            nNextStep := 20;
        END_IF
    20:
        //Execute the SQL command of sQuery
        IF fbSQLCommand.Execute(ADR(sQuery), SIZEOF(sQuery)) >= 0 THEN
            tcMessage := fbSQLCommand.ipTcResult;
            nNextStep := 99;
        END_IF
    99:
        //Disconnect from the database
        IF fbDatabase.Disconnect() THEN
            nNextStep := 0;
        END_IF
END_CASE
 
//Advance the state machine step
nStep := nNextStep;</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">//Generate the query string.</span></span>
<span class="line"><span style="color: #6A9955">//Change TableName to your table&apos;s name.</span></span>
<span class="line"><span style="color: #6A9955">//Change column1 and column2 to the column names (include any other columns your table has, separated by ,).</span></span>
<span class="line"><span style="color: #6A9955">//Change $&apos;value1$&apos; and $&apos;value2$&apos; to the values you wish to send.</span></span>
<span class="line"><span style="color: #6A9955">//Note: $ is an escape character for a single quote. Single quotes indicate a string.</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">sQuery := </span><span style="color: #CE9178">&apos;INSERT INTO TableName (column1, column2) values ($&apos;</span><span style="color: #D4D4D4">value1$</span><span style="color: #CE9178">&apos;, $&apos;</span><span style="color: #D4D4D4">value2$</span><span style="color: #CE9178">&apos;)&apos;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">CASE</span><span style="color: #D4D4D4"> nStep </span><span style="color: #569CD6">OF</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4">:</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #6A9955">//Connect to the database at database ID 1</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">IF</span><span style="color: #D4D4D4"> fbDatabase.Connect(</span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">THEN</span></span>
<span class="line"><span style="color: #D4D4D4">            nNextStep := </span><span style="color: #B5CEA8">10</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">        END_IF</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #B5CEA8">10</span><span style="color: #D4D4D4">:</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #6A9955">//Move the database message result to tcMessage</span></span>
<span class="line"><span style="color: #D4D4D4">        tcMessage := fbDatabase.ipTcResult;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #6A9955">//Go to disconnect step if there&apos;s an error</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">IF</span><span style="color: #D4D4D4"> fbDatabase.bError </span><span style="color: #569CD6">THEN</span></span>
<span class="line"><span style="color: #D4D4D4">            nNextStep := </span><span style="color: #B5CEA8">99</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">        END_IF</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #6A9955">//Create a SQL command</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">IF</span><span style="color: #D4D4D4"> fbDatabase.CreateCmd(ADR(fbSqlCommand)) </span><span style="color: #569CD6">THEN</span></span>
<span class="line"><span style="color: #D4D4D4">            nNextStep := </span><span style="color: #B5CEA8">20</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">        END_IF</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #B5CEA8">20</span><span style="color: #D4D4D4">:</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #6A9955">//Execute the SQL command of sQuery</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">IF</span><span style="color: #D4D4D4"> fbSQLCommand.Execute(ADR(sQuery), SIZEOF(sQuery)) &gt;= </span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">THEN</span></span>
<span class="line"><span style="color: #D4D4D4">            tcMessage := fbSQLCommand.ipTcResult;</span></span>
<span class="line"><span style="color: #D4D4D4">            nNextStep := </span><span style="color: #B5CEA8">99</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">        END_IF</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #B5CEA8">99</span><span style="color: #D4D4D4">:</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #6A9955">//Disconnect from the database</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">IF</span><span style="color: #D4D4D4"> fbDatabase.Disconnect() </span><span style="color: #569CD6">THEN</span></span>
<span class="line"><span style="color: #D4D4D4">            nNextStep := </span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">        END_IF</span></span>
<span class="line"><span style="color: #D4D4D4">END_CASE</span></span>
<span class="line"><span style="color: #D4D4D4"> </span></span>
<span class="line"><span style="color: #6A9955">//Advance the state machine step</span></span>
<span class="line"><span style="color: #D4D4D4">nStep := nNextStep;</span></span></code></pre></div>



<p class="wp-block-paragraph"></p>



<p class="wp-block-paragraph">The state machine will do the following:</p>



<ul class="wp-block-list">
<li>Connect to the database with database ID 1. If you have multiple databases, you can change the database ID input to point to a specific database. You can find this ID by going back to the database configuration page (where you typed in the IP address, port number, etc.) and checking what the DBID is in the top left corner.</li>



<li>Create the SQL command.</li>



<li>Execute the SQL command according to sQuery</li>



<li>Disconnect from the database</li>
</ul>



<p class="wp-block-paragraph">You can activate the configuration for the PLC project to start sending queries to the database!</p>



<h2 id="testing" class="wp-block-heading">4. Testing the Database Connection</h2>



<h3 id="h-4-1-open-tf6420-configurator" class="wp-block-heading">4.1. Open TF6420 Configurator</h3>



<p class="wp-block-paragraph">After activating the configuration, the state machine should be looping through and inserting data into our table. We can check this by opening up the TF6420 Configurator application.</p>



<figure class="wp-block-image aligncenter size-full"><img decoding="async" width="866" height="707" src="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-configurator.png" alt="" class="wp-image-34752" srcset="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-configurator.png 866w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-configurator-300x245.png 300w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-configurator-768x627.png 768w" sizes="(max-width: 866px) 100vw, 866px" /></figure>



<p class="wp-block-paragraph">The database server and configuration should already be added from our steps to install the database server. If not, refer to the&nbsp;Adding the Database Project to a TwinCAT 3 Solution section.</p>



<h3 id="h-4-2-execute-a-sql-select-query" class="wp-block-heading">4.2. Execute a SQL Select Query</h3>



<p class="wp-block-paragraph">Open the SQL Query Editor.</p>



<figure class="wp-block-image aligncenter size-full"><img decoding="async" width="866" height="316" src="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-sql-query-editor.png" alt="" class="wp-image-34753" srcset="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-sql-query-editor.png 866w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-sql-query-editor-300x109.png 300w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-sql-query-editor-768x280.png 768w" sizes="(max-width: 866px) 100vw, 866px" /></figure>



<p class="wp-block-paragraph">You should see a list of the SQL databases available from the DB configuration pool. You can click the refresh button if there are no databases currently connected. If that doesn&#8217;t work, make sure you&#8217;ve gone through the above steps and the configuration is activated.</p>



<figure class="wp-block-image aligncenter size-full"><img decoding="async" width="866" height="369" src="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-sql-databases.png" alt="" class="wp-image-34754" srcset="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-sql-databases.png 866w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-sql-databases-300x128.png 300w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-sql-databases-768x327.png 768w" sizes="(max-width: 866px) 100vw, 866px" /></figure>



<p class="wp-block-paragraph">Drop down the database to see all the available tables. You can right click on the table you&#8217;re writing to and click Select.</p>



<figure class="wp-block-image aligncenter size-full"><img decoding="async" width="866" height="406" src="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-select-table.png" alt="" class="wp-image-34755" srcset="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-select-table.png 866w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-select-table-300x141.png 300w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-select-table-768x360.png 768w" sizes="(max-width: 866px) 100vw, 866px" /></figure>



<p class="wp-block-paragraph">You can see that TF6420 has auto-generated a select statement for you. If you press the FB_SQLCommandEvt.ExecuteDataReturn button, you should see datasets populate in the results field to the right.</p>



<figure class="wp-block-image aligncenter size-full"><img decoding="async" width="866" height="383" src="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-execute-sql-statement.png" alt="" class="wp-image-34756" srcset="https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-execute-sql-statement.png 866w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-execute-sql-statement-300x133.png 300w, https://static.dmcinfo.com/wp-content/uploads/2024/05/tf6420-execute-sql-statement-768x340.png 768w" sizes="(max-width: 866px) 100vw, 866px" /></figure>



<p class="wp-block-paragraph">If these are the values you&#8217;re sending to the database then the database server is communicating properly!</p>



<p class="wp-block-paragraph">For more information on some of the topics covered in this blog, please see the following:</p>



<ul class="wp-block-list">
<li><a href="https://infosys.beckhoff.com/content/1033/tf6420_tc3_database_server/index.html?id=3802295219214861788" target="_blank" rel="noreferrer noopener">TF6420 Database Server Infosys Page</a></li>



<li><a href="https://infosys.beckhoff.com/content/1033/tf6420_tc3_database_server/2674353931.html?id=997028420171989533" target="_blank" rel="noreferrer noopener">SQL Expert Mode Function Block Infosys Page</a></li>



<li><a href="https://www.postgresql.org/docs/" target="_blank">PostgreSQL Documentation</a></li>



<li><a href="https://static.dmcinfo.com/blog/19695/getting-started-with-a-beckhoff-plc-part-one-setup/">Getting Started with a Beckhoff PLC</a></li>
</ul>



<p class="wp-block-paragraph">For information that was not discussed directly in this blog&nbsp;but could be good next steps, please see the following:</p>



<ul class="wp-block-list">
<li><a href="https://static.dmcinfo.com/blog/26400/ms-sql-stored-procedures-directly-from-beckhoff-twincat-plc/">MS SQL Stored Procedures Directly from Beckhoff TwinCAT PLC</a></li>



<li><a href="https://static.dmcinfo.com/blog/17976/getting-started-with-opc-ua-in-beckhoff-twincat-3/">Getting Started with OPC UA in Beckhoff TwinCAT 3</a></li>



<li><a href="https://static.dmcinfo.com/blog/16708/sql-server-performance-troubleshooting/">SQL Server Performance Troubleshooting</a></li>



<li><a href="https://infosys.beckhoff.com/content/1033/tf6420_tc3_database_server/5875128203.html?id=7303488825076816628" target="_blank" rel="noreferrer noopener">NoSQL Expert mode Function Block Infosys Page</a></li>
</ul>



<div class="wp-block-group alignwide has-custom-light-blue-background-color has-background is-layout-flow wp-container-core-group-is-layout-dbd34961 wp-block-group-is-layout-flow" style="border-radius:20px;margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50);padding-top:var(--wp--preset--spacing--50);padding-right:0;padding-bottom:var(--wp--preset--spacing--50);padding-left:0">
<div class="wp-block-columns alignwide are-vertically-aligned-center is-layout-flex wp-container-core-columns-is-layout-43efaee5 wp-block-columns-is-layout-flex" style="padding-right:var(--wp--preset--spacing--60);padding-left:var(--wp--preset--spacing--60)">
<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:85%">
<h3 class="wp-block-heading has-text-align-left" id="h-have-an-upcoming-project-dmc-can-help-you-take-the-next-step"><strong>Connect TwinCAT 3 Directly to Your Database</strong>.</h3>



<p class="has-text-align-left wp-block-paragraph" id="h-need-help-turning-ideas-into-outcomes-automation-project-to-the-next-level-contact-us-today-to-learn-more-about-our-solutions-and-how-we-can-help-you-achieve-your-goals">Explore our <a href="https://static.dmcinfo.com/services/manufacturing-automation-and-intelligence/" data-type="page" data-id="420">Automation</a> expertise in <a href="https://static.dmcinfo.com/services/manufacturing-automation-and-intelligence/plc-programming/beckhoff-and-twincat-3-programming/" data-type="page" data-id="457">Beckhoff TwinCAT 3</a>, TF6420 Database Server, PostgreSQL, and PLC-based SQL integration.</p>
</div>



<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:15%">
<div class="wp-block-buttons is-horizontal is-content-justification-center is-layout-flex wp-container-core-buttons-is-layout-2236275c wp-block-buttons-is-layout-flex">
<div class="wp-block-button is-style-fill"><a class="wp-block-button__link has-base-contrast-color has-text-color has-link-color wp-element-button" href="https://static.dmcinfo.com/contact/">Contact Us</a></div>
</div>
</div>
</div>
</div>
<p>The post <a href="https://static.dmcinfo.com/blog/16328/getting-started-with-database-server-in-beckhoff-twincat-3/">Getting Started with Database Server in Beckhoff TwinCAT 3</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
