{"id":77943,"date":"2018-04-10T00:17:51","date_gmt":"2018-04-10T00:17:51","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=77943"},"modified":"2021-09-29T16:21:03","modified_gmt":"2021-09-29T16:21:03","slug":"sql-server-graph-databases-part-3-modifying-data-graph-database","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/t-sql-programming-sql-server\/sql-server-graph-databases-part-3-modifying-data-graph-database\/","title":{"rendered":"SQL Server Graph Databases \u2013 Part 3: Modifying Data in a Graph Database"},"content":{"rendered":"<h4>The series so far:<\/h4>\n<ol>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/sql-development\/sql-server-graph-databases-part-1-introduction\/\">SQL Server Graph Databases - Part 1: Introduction<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/t-sql-programming\/sql-server-graph-databases-part-2-querying-data-graph-database\/\">SQL Server Graph Databases - Part 2: Querying Data in a Graph Database<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/t-sql-programming\/sql-server-graph-databases-part-3-modifying-data-graph-database\/\">SQL Server Graph Databases - Part 3: Modifying Data in a Graph Database<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/t-sql-programming\/sql-server-graph-databases-part-4-working-hierarchical-data-graph-database\">SQL Server Graph Databases - Part 4: Working with hierarchical data in a graph database<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/t-sql-programming\/sql-server-graph-databases-part-5-importing-relational-data-graph-database\/\">SQL Server Graph Databases - Part 5:\u00a0Importing Relational Data into a Graph Database<\/a><\/li>\n<\/ol>\n\n<p>SQL Server 2017 makes it possible to implement graph databases within a relational table structure, allowing you define complex relationships between data much easier than with traditional approaches. Under this model, nodes (entities) and edges (relationships) are implemented as tables in a user-defined database, with the graph features integrated into the database engine. As a result, you can use familiar T-SQL statements to work with the graph tables, and you can use the graph features in conjunction with other SQL Server components and tools, such as columnstore indexes, Machine Learning Services, and SQL Server Management Studio (SSMS).<\/p>\n<p>This article is the third in a series that covers different aspects of SQL Server graph databases. In the first two articles, you learned how to create, populate and query node and edge tables. In this article, you\u2019ll learn how to delete and update graph data, as well as how to insert data in ways we have not yet covered.<\/p>\n<p>Like the previous articles, this article includes a number of examples that target the <strong>FishGraph<\/strong> database, which is based on a fictitious fish-lovers forum. The database includes the <strong>FishSpecies<\/strong>, <strong>FishLover<\/strong>, and <strong>FishPost<\/strong> node tables and the <strong>Likes<\/strong>, <strong>Posts<\/strong>, and <strong>LinksTo<\/strong> edge tables. The following figure shows the data model used to build the database.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1116\" height=\"686\" class=\"wp-image-77944\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/word-image-1.png\" \/><\/p>\n<p>If you worked through the first two articles, this should all look familiar to you. The rectangles represent the nodes, and the arrows connecting the nodes represent the edges, with the arrows pointing in the direction of the relationships. You can download the T-SQL script used to create and populate the database at the bottom of this article.<\/p>\n<p>Because we covered how to insert data in the first article, we\u2019ll start this article with how to delete graph data and then move on to some new tricks for inserting data. We\u2019ll leave data updates till the end because they\u2019re not quite as straightforward as with inserting or deleting data.<\/p>\n<h2>Deleting Graph Data<\/h2>\n<p>For the most part, deleting data from a node or edge table is just like deleting data from any other table. Things only get sticky if you base your deletion on any of the auto-defined columns (<strong>node_id<\/strong>, <strong>$edge_id<\/strong>, <strong>$from_id<\/strong>, or <strong>$to_id<\/strong>), but even then, it\u2019s not all that complicated.<\/p>\n<p>Before we get into the nitty gritty of data deletion, first insert a row into the <strong>Likes<\/strong> table:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">INSERT INTO Likes ($from_id, $to_id) VALUES (\r\n(SELECT $node_id FROM FishLover WHERE FishLoverID = 2), \r\n(SELECT $node_id FROM FishPost WHERE PostID = 3));<\/pre>\n<p>The <strong>INSERT<\/strong> statement adds the relationship <em>fish lover 2 likes fish post 3<\/em>. You can verify that the relationship has been properly created by running the following <strong>SELECT<\/strong> statement:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">SELECT Lover.Username, Post.Title\r\nFROM FishLover Lover, Likes, FishPost Post\r\nWHERE MATCH(Lover-(Likes)-&gt;Post)\r\n   AND Lover.FishLoverID = 2\r\n   AND Post.PostID = 3;<\/pre>\n<p>The <strong>SELECT<\/strong> statement uses the <strong>MATCH<\/strong> function to return only those rows in which a fish lover likes a fish post. The other two <strong>WHERE<\/strong> clause conditions refine the search to the specific user and post, giving us the following results.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"584\" height=\"200\" class=\"wp-image-77945\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/word-image-2.png\" \/><\/p>\n<p>I won\u2019t go into much detail about the <strong>INSERT<\/strong> and <strong>SELECT<\/strong> statements because I covered how to add and query data in the first two articles, at least to the extent that these statements are used here. If you have any questions about what\u2019s going on, be sure to refer back to those articles.<\/p>\n<p>With that in mind, let\u2019s look at how to delete the relationship you just added. One approach is to use subqueries in the <strong>WHERE<\/strong> clause to retrieve the <strong>$node_id<\/strong> values from the originating and terminating nodes (to try out the DELETE statements, just rerun the INSERT statement as needed):<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">DELETE Likes\r\nWHERE $from_id = (SELECT $node_id FROM FishLover WHERE FishLoverID = 2)\r\n  AND $to_id = (SELECT $node_id FROM FishPost WHERE PostID = 3);<\/pre>\n<p>Because the deletion is based on the values in the <strong>$from_id<\/strong> and <strong>$to_id<\/strong> columns in the <strong>Likes<\/strong> table, you need to compare them to <strong>$node_id<\/strong> values from the originating and terminating records. As you\u2019ll recall from the first article, the database engine automatically generates the <strong>$node_id<\/strong> values in the node tables, creating each one as a JSON string that provides the type (node or edge), schema, table, and a <strong>BIGINT<\/strong> value unique to each row.<\/p>\n<p>Of course, you can pass in the <strong>$node_id<\/strong> values as literal stings in the <strong>WHERE<\/strong> clause, just like you can pass in a literal string if comparing the <strong>$edge_id<\/strong> column (which we\u2019ll get to shortly). However, building the JSON strings is not always as convenient as constructing subqueries. The key to using subqueries is being able to target the correct records in the originating and terminating nodes. In this case, we\u2019re able to use the <strong>FishLoverID<\/strong> and <strong>PostID<\/strong> values from the node tables.<\/p>\n<p>However, this is not the only approach you can take to deleting the data. You can also use the <strong>MATCH<\/strong> function to specify which relationship to delete, as shown in the following example:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">DELETE Likes\r\nFROM FishLover Lover, Likes, FishPost Post\r\nWHERE MATCH(Lover-(Likes)-&gt;Post)\r\n AND Lover.FishLoverID = 2\r\n AND Post.PostID = 3;<\/pre>\n<p>As you\u2019ll recall from the second article, the <strong>MATCH<\/strong> function lets you define a search pattern based on the relationships between nodes. You can use the function only in the <strong>WHERE<\/strong> clause when querying a node or edge table. In this case, the function filters out all rows except for those in which a fish lover likes a fish post. The other <strong>WHERE<\/strong> clause conditions ensure that that the correct relationship is deleted.<\/p>\n<p>You can achieve the same results by joining the tables and foregoing the <strong>MATCH<\/strong> function altogether:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">DELETE lk\r\nFROM Likes lk INNER JOIN FishLover fl\r\n   ON lk.$from_id = fl.$node_id\r\nINNER JOIN FishPost fp\r\n   ON lk.$to_id = fp.$node_id\r\nWHERE fl.FishLoverID = 2 AND fp.PostID = 3;<\/pre>\n<p>All we\u2019re doing here is joining the two node tables and one edge table. The <strong>FROM<\/strong> clause joins the tables based on <strong>$node_id<\/strong> values, and the <strong>WHERE<\/strong> clause filters the data based on the <strong>FishLoverID<\/strong> and <strong>PostID<\/strong> values. Although this statement is a bit more cumbersome that the preceding example, it uses a traditional T-SQL statement to delete the data, an approach that\u2019s familiar to many.<\/p>\n<h2>System Functions for Graph Database<\/h2>\n<p>As mentioned above, you can also delete a relationship from an edge table by comparing the <strong>$edge_id<\/strong> column to a literal JSON value, as shown in the following example:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">DELETE Likes\r\nWHERE $edge_id = \r\n   '{\"type\":\"edge\",\"schema\":\"dbo\",\"table\":\"Likes\",\"id\":15}';<\/pre>\n<p>This approach works fine as long as you don\u2019t mind having to construct a JSON snippet every time you want to delete a row. However, SQL Server 2017 also comes with a set of built-in system functions that help simplify this process. For example, you can use the <strong>EDGE_ID_FROM_PARTS<\/strong> function to construct the edge ID based on the SQL Server object ID and graph ID, as shown in the following <strong>DELETE<\/strong> statement:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">DECLARE @obj INT = OBJECT_ID('dbo.Likes');\r\nDELETE Likes\r\nWHERE $edge_id = EDGE_ID_FROM_PARTS(@obj, 15);<\/pre>\n<p>The object ID is the unique integer assigned to each object in a SQL Server database. The graph ID is the unique integer assigned to each <strong>$edge_id<\/strong> value (the <strong>id<\/strong> element). The <strong>id<\/strong> value works much like the values assigned to an <strong>IDENTITY<\/strong> column, with the value incremented by one with each insertion.<\/p>\n<p>For this example, I used the <strong>OBJECT_ID<\/strong> function to retrieve the object ID for the <strong>Likes<\/strong> table and then assigned that value to the <strong>@obj<\/strong> variable, which I then passed in as the first argument when calling the <strong>EDGE_ID_FROM_PARTS<\/strong> function. You do not need to do this in a variable, but it can help to make the code more readable.<\/p>\n<p>For the second argument, I passed in a value of <strong>15<\/strong> as the <strong>id<\/strong> portion of the JSON value. The key to make sure you\u2019re passing in the correct <strong>id<\/strong> value. Otherwise, that\u2019s all there is to using the <strong>EDGE_ID_FROM_PARTS<\/strong> function, and the other graph functions are just as basic. SQL Server 2017 includes six graph functions in all, as described in the following table.<\/p>\n<p>Built-in functionDescription<\/p>\n<table class=\"table--tight\">\n<tbody>\n<tr>\n<td class=\"padded-h--loose\">\n<p><strong>OBJECT_ID_FROM_NODE_ID<\/strong><\/p>\n<\/td>\n<td class=\"padded-h--loose\">\n<p>Extracts the object ID from a <strong>$node_id<\/strong> value.<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td class=\"padded-h--loose\">\n<p><strong>GRAPH_ID_FROM_NODE_ID<\/strong><\/p>\n<\/td>\n<td class=\"padded-h--loose\">\n<p>Extracts the graph ID from a <strong>$node_id<\/strong> value.<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td class=\"padded-h--loose\">\n<p><strong>NODE_ID_FROM_PARTS<\/strong><\/p>\n<\/td>\n<td class=\"padded-h--loose\">\n<p>Constructs a JSON node ID from an object ID and graph ID.<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td class=\"padded-h--loose\">\n<p><strong>OBJECT_ID_FROM_EDGE_ID<\/strong><\/p>\n<\/td>\n<td class=\"padded-h--loose\">\n<p>Extracts the object ID from an <strong>$edge_id<\/strong> value.<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td class=\"padded-h--loose\">\n<p><strong>GRAPH_ID_FROM_EDGE_ID<\/strong><\/p>\n<\/td>\n<td>\n<p>Extracts the graph ID from an <strong>$edge_id<\/strong> value.<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td class=\"padded-h--loose\">\n<p><strong>EDGE_ID_FROM_PARTS<\/strong><\/p>\n<\/td>\n<td class=\"padded-h--loose\">\n<p>Constructs a JSON edge ID from an object ID and graph ID.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Microsoft currently provides little information about these functions and offers no examples, but if you play around with them, you\u2019ll quickly get a sense of how they work. No doubt we\u2019ll eventually see more information about each function, and we might even see additional functions. Until then, the existing ones represent a good starting point, not only for deleting data, but also for building other types of queries, as you\u2019ll see in the next section.<\/p>\n<h2>Revisiting Data Inserts and Data Queries<\/h2>\n<p>In the first article in this series, you saw several examples of how to insert data into node and edge tables. Inserting data into a node table works just like any other SQL Server table. You specify the target columns and their values and leave it to the database engine to populate the <strong>$node_id<\/strong> column.<\/p>\n<p>If you want to remove the new row so you can try different ways to insert the same data, you can use the following <strong>DELETE<\/strong> statement as needed, specifying the correct <strong>FishLoverID<\/strong> and <strong>PostID<\/strong> values:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">DELETE Likes\r\nFROM FishLover Lover, Likes, FishPost Post\r\nWHERE MATCH(Lover-(Likes)-&gt;Post)\r\n  AND Lover.FishLoverID = 2\r\n  AND Post.PostID = 3;<\/pre>\n<p>Edge tables are similar in this respect, except that the database provides values for the <strong>$edge_id<\/strong> column. However, edge tables also include the <strong>$from_id<\/strong> and <strong>$to_id<\/strong> columns, which you must manually populate when you insert data. One way to do this is to use subqueries that retrieve the <strong>$node_id<\/strong> values from the originating and terminating node tables, as you saw earlier in the article (duplicated here for your convenience):<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">INSERT INTO Likes ($from_id, $to_id) VALUES (\r\n  (SELECT $node_id FROM FishLover WHERE FishLoverID = 2), \r\n  (SELECT $node_id FROM FishPost WHERE PostID = 3));<\/pre>\n<p>Although this approach works fine, you can instead use the <strong>NODE_ID_FROM_PARTS<\/strong> function to construct the <strong>$node_id<\/strong> values when inserting the data:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">DECLARE @obj1 INT = OBJECT_ID('dbo.FishLover');\r\nDECLARE @obj2 INT = OBJECT_ID('dbo.FishPost');\r\nINSERT INTO Likes ($from_id, $to_id) VALUES (\r\n  NODE_ID_FROM_PARTS(@obj1, 1), \r\n  NODE_ID_FROM_PARTS(@obj2, 2));<\/pre>\n<p>The example first obtains the object IDs from the relationship\u2019s originating and terminating node tables and saves the IDs to the <strong>@obj1<\/strong> and <strong>@obj2<\/strong> variables, which are then used when calling the <strong>NODE_ID_FROM_PARTS<\/strong> function. (Again, you do not need to use variables.)<\/p>\n<p>For the second function argument, you should use the graph IDs from the <strong>$node_id<\/strong> values in the originating and terminating node tables. These values are different from the <strong>FishLoverID<\/strong> and <strong>PostID<\/strong> columns because SQL Server starts at <strong>0<\/strong> when assigning the graph ID to a <strong>$node_id<\/strong> or <strong>$edge_id<\/strong> value.<\/p>\n<p>You can verify that the relationship has been correctly added to the <strong>Likes<\/strong> table by running the same query as earlier:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">SELECT Lover.Username, Post.Title\r\nFROM FishLover Lover, Likes, FishPost Post\r\nWHERE MATCH(Lover-(Likes)-&gt;Post)\r\n  AND Lover.FishLoverID = 2\r\n  AND Post.PostID = 3;<\/pre>\n<p>If everything is working as expected, you should see the results shown in the following figure.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"584\" height=\"200\" class=\"wp-image-77946\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/word-image-3.png\" \/><\/p>\n<p>Up to this point in the series, the example <strong>INSERT<\/strong> statements left it to the database engine to generate the <strong>$node_id<\/strong> and <strong>$edge_id<\/strong> values; however, you can specify those values when inserting data. For example, the following T-SQL retrieves the object ID for the <strong>FishPost<\/strong> table and uses it to construct the <strong>$node_id<\/strong> value for the new row:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">DECLARE @obj INT = OBJECT_ID('dbo.FishPost');\r\nINSERT INTO FishPost ($node_id, Title, MessageText) VALUES\r\n  (NODE_ID_FROM_PARTS(@obj, 106), 'A day in the life of fish', \r\n  'Donec pede justo, porttitor eu, consequat vitae, eleifend ac, enim.');<\/pre>\n<p>By taking this approach, you can specify what integer value to assign as the graph ID used to build the node ID (in this case, <strong>106<\/strong>). This can be handy if you\u2019re pulling data in from another source and want to use the existing ID assigned to each row as the graph ID. Just be sure to use a unique value for each graph ID, or the database engine will return an error.<\/p>\n<p>Also note that SQL Server documentation briefly mentions that you cannot insert data into the <strong>$node_id<\/strong> or <strong>$edge_id<\/strong> column of a graph table. If you do, according to Microsoft, you\u2019ll receive an error. This has not been my experience. I\u2019ve had no problem inserting the ID values, as long as I pass them in using the correct JSON form. Because graph databases are so new to SQL Server, it\u2019s not surprising to run into these types of inconsistencies.<\/p>\n<p>With that in mind, you can verify that the row has been inserted into the <strong>FishPost<\/strong> statement by running the following <strong>SELECT<\/strong> statement:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">SELECT GRAPH_ID_FROM_NODE_ID($node_id) AS NodeID, PostID, Title \r\nFROM FishPost;<\/pre>\n<p>Notice that the <strong>SELECT<\/strong> clause uses the <strong>GRAPH_ID_FROM_NODE_ID<\/strong> function to return only the graph ID from each <strong>$node_id<\/strong> value, making it easier to confirm the value we just added. The following figure shows what the <strong>FishPost<\/strong> table should look like at this point.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"728\" height=\"422\" class=\"wp-image-77947\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/word-image-4.png\" \/><\/p>\n<p>Suppose you now want to return only the row that contains the last graph ID value inserted into the table. This can be a little tricky because SQL Server sometimes treats this type of table as though it includes two <strong>IDENTIY<\/strong> columns, in this case, <strong>$node_id<\/strong> and <strong>PostID<\/strong>, even though a SQL Server table can theoretically include with only one such column.<\/p>\n<p>One approach you might consider to return the row is to use the <strong>@@IDENTITY<\/strong> system function in your <strong>WHERE<\/strong> clause:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">SELECT GRAPH_ID_FROM_NODE_ID($node_id) AS NodeID, PostID, Title \r\nFROM FishPost\r\nWHERE GRAPH_ID_FROM_NODE_ID($node_id) = \r\n  (SELECT @@IDENTITY);<\/pre>\n<p>The <strong>@@IDENTITY<\/strong> function returns the last identity value inserted into an identity column in the current session, regardless of where the value was inserted. In this case, the function is used to compare the graph ID to the last inserted identity value. If the values match, the row is returned, as shown in the following figure.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"754\" height=\"252\" class=\"wp-image-77948\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/word-image-5.png\" \/><\/p>\n<p>Because the <strong>SELECT<\/strong> statement returned the expected row, you know that the <strong>@@IDENTITY<\/strong> function returned a value of <strong>106<\/strong>, the last inserted graph ID. (Note that you might see a different value for the <strong>PostID<\/strong> column if you inserted other data in the <strong>FishPost<\/strong> table.) You can verify that <strong>106<\/strong> is being returned by using the following statement:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">SELECT @@IDENTITY;<\/pre>\n<p>Assuming you didn\u2019t run any other <strong>INSERT<\/strong> statements in your last session, you should receive the correct value.<\/p>\n<p>You might also consider using the <strong>IDENT_CURRENT<\/strong> function rather than the <strong>@@IDENTITY<\/strong> function because you can target a specific table or view and it\u2019s not limited to the current session. When calling the <strong>IDENT_CURRENT<\/strong> function, you must provide the name of the target table, as shown in the following example:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">SELECT GRAPH_ID_FROM_NODE_ID($node_id) AS NodeID, PostID, Title \r\nFROM FishPost\r\nWHERE GRAPH_ID_FROM_NODE_ID($node_id) = \r\n  (SELECT IDENT_CURRENT('FishPost'));<\/pre>\n<p>Unfortunately, this approach generates no results because, in this case, the database engine returns the last value inserted into the <strong>PostID<\/strong> column. You can confirm this by running the following <strong>SELECT<\/strong> statement:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">SELECT IDENT_CURRENT('FishPost')<\/pre>\n<p>The statement returns a value of <strong>6<\/strong> rather than <strong>106<\/strong>, which is why the previous <strong>SELECT<\/strong> statement returns no rows.<\/p>\n<p>When I tried all this out, I expected the <strong>@@IDENTITY<\/strong> and <strong>IDENT_CURRENT<\/strong> functions to return the same value and could find nothing in the Microsoft fine print to suggest why they might be different. Things got even stranger when I created a node table that did not include an <strong>IDENTITY<\/strong> column. This time both the <strong>@@IDENTITY<\/strong> function and <strong>IDENT_CURRENT<\/strong> function returned a <strong>NULL<\/strong> value after I inserted a row. Next, I created a table that included the <strong>IDENTITY<\/strong> column but inserted a row without specifying the <strong>$node_id<\/strong> value. In this case, both functions returned a value of <strong>1<\/strong>. In addition, my results were always the same whether or not I called the identify functions within the same batch as the <strong>INSERT<\/strong> statement.<\/p>\n<p>Apparently, Microsoft still has some details to work out with graph databases, or I\u2019m missing something important about how identity functions work. Just in case it\u2019s not me, you might want to proceed with caution when using identity functions in conjunction with graph databases.<\/p>\n<p>In the meantime, it you want to delete the row you just added, you can use the <strong>GRAPH_ID_FROM_NODE_ID<\/strong> function to extract the graph ID when comparing it to the value <strong>106<\/strong>:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">DELETE FishPost WHERE GRAPH_ID_FROM_NODE_ID($node_id) = 106;<\/pre>\n<p>Clearly, the graph-related functions can come in handy at times, depending on the type of queries you\u2019re trying to perform.<\/p>\n<h2>Data Updates Not Allowed<\/h2>\n<p>Unfortunately, the process of updating data in a graph database is not as straightforward as adding or deleting data. That\u2019s not to say you can\u2019t perform any updates, you just can\u2019t perform them on the auto-defined graph columns (<strong>$node_id<\/strong>, <strong>$edge_id<\/strong>, <strong>$from_id<\/strong>, and <strong>$to_id<\/strong>).<\/p>\n<p>To test this out, start with the following <strong>INSERT<\/strong> statement, which adds a row to the <strong>FishPost<\/strong> table:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk \">DECLARE @obj INT = OBJECT_ID('dbo.FishPost');\r\nINSERT INTO FishPost ($node_id, Title, MessageText) VALUES\r\n (NODE_ID_FROM_PARTS(@obj, 107), 'Another day, another fish', \r\n 'Donec pede justo, porttitor eu, consequat vitae, eleifend ac, enim.');<\/pre>\n<p>If you now want to modify data in one of the user-defined columns, you can run an <strong>UPDATE<\/strong> statement just like you would on any table. For example, the following <strong>UPDATE<\/strong> statement modifies the title of the row you just inserted:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">UPDATE FishPost\r\nSET Title = 'Another data of fishing'\r\nWHERE GRAPH_ID_FROM_NODE_ID($node_id) = 107;<\/pre>\n<p>If you were to query this row, you would see that the title has been updated. However, suppose you now want to update the <strong>$node_id<\/strong> value by changing the graph ID to <strong>207<\/strong>:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">UPDATE FishPost\r\nSET $node_id = NODE_ID_FROM_PARTS(OBJECT_ID('FishPost'), 207)\r\nWHERE GRAPH_ID_FROM_NODE_ID($node_id) = 107;<\/pre>\n<p>This time, the <strong>UPDATE<\/strong> statement returns the following error on my system:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"627\" height=\"53\" class=\"wp-image-77949\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/word-image-6.png\" \/><\/p>\n<p>The same goes for trying to update the <strong>$edge_id<\/strong> value in an edge table:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">UPDATE Likes\r\nSET $edge_id = EDGE_ID_FROM_PARTS(OBJECT_ID('Likes'), 216)\r\nWHERE GRAPH_ID_FROM_EDGE_ID($edge_id) = 16;<\/pre>\n<p>The statement generates the same type of error as the previous statement, and the same limitation holds true for the <strong>$from_id<\/strong> and <strong>$to_id<\/strong> columns, even though these are not computed columns and you provide the values yourself. For example, the following <strong>UPDATE<\/strong> statement tries to update a relationship in the <strong>Likes<\/strong> table:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">  UPDATE Likes\r\n  SET $from_id = NODE_ID_FROM_PARTS(OBJECT_ID('FishLover'), 3)\r\n  WHERE GRAPH_ID_FROM_EDGE_ID($edge_id) = 16;<\/pre>\n<p>Once again, you get the same error. In fact, the only way you can update an auto-defined graph column is to first delete the applicable record and then insert a new record with the correct data, as shown in the following example:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk \">DELETE FishPost\r\nWHERE GRAPH_ID_FROM_NODE_ID($node_id) = 107; \r\nINSERT INTO FishPost ($node_id, Title, MessageText) VALUES\r\n  (NODE_ID_FROM_PARTS(OBJECT_ID('dbo.FishPost'), 207), \r\n  'Another day, another fish', \r\n  'Donec pede justo, porttitor eu, consequat vitae, enim.');<\/pre>\n<p>Although the graph ID value is the only data that is changing, the entire record must be deleted and a new one added. To confirm that the record has been updated, you can use the following <strong>SELECT<\/strong> statement:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk \">SELECT GRAPH_ID_FROM_NODE_ID($node_id) AS NodeID, PostID, Title \r\nFROM FishPost\r\nWHERE GRAPH_ID_FROM_NODE_ID($node_id) = 207;<\/pre>\n<p>The statement returns the results shown in the following figure, which indicates a <strong>NodeID<\/strong> value of <strong>207<\/strong>.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"850\" height=\"256\" class=\"wp-image-77950\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/word-image-7.png\" \/><\/p>\n<p>As long as you know how to delete and insert records, you should have no problem performing your updates. Although this approach adds a bit of complexity to the process, it\u2019s not too bad overall. Just be sure you delete and add the data within a single transaction.<\/p>\n<h2>Modifying Data in a Graph Database<\/h2>\n<p>For the most part, working with graph databases is fairly straightforward, once you figure out the basics of querying node and edge tables. The graph-related functions can help, but so can understanding how these tables work together to define complex relationships.<\/p>\n<p>Be aware, however, that the graph database features do not include a mechanism for validating relationships. For example, you can create relationships where they don\u2019t belong, such as a fish species liking a fish post. You can also delete a relationship\u2019s originating or terminating record without deleting the relationships itself. For a more complete discussion of these issues, check out Dennes Torres\u2019s excellent article <a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/t-sql-programming\/sql-graph-objects-sql-server-2017-good-bad\/\">SQL Graph Objects in SQL Server 2017: the Good and the Bad<\/a>.<\/p>\n<p>Because the tables in a graph database are similar to typical SQL Server tables, you can usually get around most limitations, as long as you understand the basics of querying and modify graph data. And the best way to learn those basics is to try the graph database features for yourself, using the type of real-world data that drives the applications you\u2019re supporting.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Modifying data in Graph Database tables is similar to working with traditional tables, but there are several things to consider. Robert Sheldon explains how to insert, update, and delete rows of Graph Database tables and explains how to use several new helper functions.  &hellip;<\/p>\n","protected":false},"author":221841,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143531],"tags":[5134],"coauthors":[6779],"class_list":["post-77943","post","type-post","status-publish","format-standard","hentry","category-t-sql-programming-sql-server","tag-sql-prompt"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/77943","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/221841"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=77943"}],"version-history":[{"count":12,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/77943\/revisions"}],"predecessor-version":[{"id":77952,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/77943\/revisions\/77952"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=77943"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=77943"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=77943"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=77943"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}