{"id":15056724,"url":"https://github.com/datastaxdevs/workshop-intro-to-cassandra","last_synced_at":"2025-10-04T16:31:50.153Z","repository":{"id":49419919,"uuid":"289985984","full_name":"datastaxdevs/workshop-intro-to-cassandra","owner":"datastaxdevs","description":"Learn Apache Cassandra fundamentals in this hands-on workshop","archived":true,"fork":false,"pushed_at":"2024-10-01T16:38:34.000Z","size":26803,"stargazers_count":219,"open_issues_count":359,"forks_count":170,"subscribers_count":19,"default_branch":"master","last_synced_at":"2025-01-23T12:33:49.931Z","etag":null,"topics":["cassandra","database","nosql","workshop"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/datastaxdevs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-08-24T16:51:37.000Z","updated_at":"2024-10-01T16:38:59.000Z","dependencies_parsed_at":"2023-01-19T12:45:14.715Z","dependency_job_id":null,"html_url":"https://github.com/datastaxdevs/workshop-intro-to-cassandra","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/datastaxdevs/workshop-intro-to-cassandra","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datastaxdevs%2Fworkshop-intro-to-cassandra","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datastaxdevs%2Fworkshop-intro-to-cassandra/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datastaxdevs%2Fworkshop-intro-to-cassandra/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datastaxdevs%2Fworkshop-intro-to-cassandra/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/datastaxdevs","download_url":"https://codeload.github.com/datastaxdevs/workshop-intro-to-cassandra/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datastaxdevs%2Fworkshop-intro-to-cassandra/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278342686,"owners_count":25971396,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-04T02:00:05.491Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cassandra","database","nosql","workshop"],"created_at":"2024-09-24T21:55:51.248Z","updated_at":"2025-10-04T16:31:49.351Z","avatar_url":"https://github.com/datastaxdevs.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🎓🔥 Intro to Apache Cassandra for Developers 🔥🎓\n\nWelcome to the 'Intro to Cassandra for Developers' workshop! In this two-hour workshop, the Developer Advocate team of DataStax shows the most important fundamentals and basics of the powerful distributed NoSQL database Apache Cassandra. Using Astra DB, the cloud based Cassandra-as-a-Service platform delivered by DataStax, we will cover the very first steps for every developer who wants to try to learn a new database: creating tables and CRUD operations. \n\nIt doesn't matter if you join our workshop live or you prefer to do at your own pace, we have you covered. In this repository, you'll find everything you need for this workshop:\n\n- Materials used during presentations\n- Hands-on exercises (see below)\n- [Workshop video](https://www.youtube.com/watch?v=wOyQlbFM1Uk)\n- [Discord chat](https://dtsx.io/discord)\n- [Questions and Answers](https://community.datastax.com/)\n\n## Homework\n\nTo complete the workshop and get a verified badge, follow these simple steps:\n\n- Watch the workshop live or recorded.\n- Complete the workshop practice as described below and make the screenshot of the last step (result of the `DELETE` in \"Execute CRUD\", see [here](#homework-note)).\n- Complete the mini-course [Cassandra Query Language](https://killercoda.com/datastaxdevs/course/cassandra-fundamentals/cql) and take a screenshot of the final screen (the one with buttons \"Back\"/\"Restart\" ... + console on the right).\n- Complete the mini-course \"Cassandra Data Modeling / Digital Library\": [lessons](https://www.datastax.com/learn/data-modeling-by-example/digital-library-data-model) and [practice](https://killercoda.com/datastaxdevs/course/cassandra-data-modeling/music-data). Take a screenshot of the final screen of the practice, with the console output at the right.\n- [Submit the Homework through this form](https://dtsx.io/homework-intro-to-cassandra) and attach the screenshot(s) you took.\n- Give us a few days to review your submission and relax: your well-earned badge will soon land in your mailbox!\n\n## Table of Contents\n\n| Title  | Description\n|---|---|\n| **Slide deck** | [Slide deck for the workshop](slides/Presentation.pdf) |\n| **1. Create your Astra DB instance** | [Create your Astra DB instance](#1-create-your-astra-db-instance) |\n| **2. Create tables** | [Create tables](#2-create-tables) |\n| **3. Execute CRUD (Create, Read, Update, Delete) operations** | [Execute CRUD operations](#3-execute-crud-operations) |\n\n\n## 1. Create your Astra DB instance\n\n_**`ASTRA DB`** is the simplest way to run Cassandra with zero operations at all - just push the button and get your cluster. No credit card required, $25.00 USD credit every month, meaning 20M read/write operations and about 80GB storage monthly - sufficient to run small production workloads._\n\n✅ Register (if needed) and Sign In to Astra DB [https://astra.datastax.com](https://astra.datastax.com): You can use your `Github`, `Google` accounts or register with an `email`.\n\n_Make sure to chose a password with minimum 8 characters, containing upper and lowercase letters, at least one number and special character_\n\n✅ Choose \"Start Free Now\"\n\nChoose the \"Start Free Now\" plan, then \"Get Started\" to work in the free tier.\n\nYou will have plenty of free initial credit (renewed each month!), roughly corresponding\nto 80 GB of storage and 20M read/write operations.\n\n\u003e If this is not enough for you, congratulations! You are most likely running a mid- to large-sized business! In that case you should switch to a paid plan.\n\n(You can follow this [guide](https://docs.datastax.com/en/astra/docs/creating-your-astra-database.html) to set up your free-tier database with the $25 monthly credit.)\n\n![astra-db-signup](images/tutorials/astra_signup.gif)\n\nTo create the database, please note that _the `db_name` and `ks_name` in the above image are just placeholders_:\n\n- **For the database name** - use `workshops`. While Astra DB allows you to fill in these fields with values of your own choosing, please follow our recommendations to ensure the application runs properly.\n\n- **For the keyspace name** - use `chatsandra`. Please stick to this name, it will make the following steps much easier (you have to customize here and there otherwise). In short:\n\n_Note_: if you already have a `workshops` database, for instance from a previous workshop with us, you can simply create the keyspace with the `Add Keyspace` button in your Astra DB dashboard: the new keyspace will be available in few seconds.\n\n| Parameter | Value \n|---|---|\n| Database name | workshops  |\n| Keyspace name | chatsandra |\n\n- **For provider and region**: Choose any provider (either GCP, AWS or Azure). Region is where your database will reside physically (choose one close to you or your users).\n\n- **Create the database**. Review all the fields to make sure they are as shown, and click the `Create Database` button.\n\nYou will see your new database as `Pending` in the Dashboard;\nthe status will change to `Active` when the database is ready. This will only take 2-3 minutes\n(you will also receive an email when it is ready).\n\n\u003e **⚠️ Important**\n\u003e ```\n\u003e The instructor might show you on screen how to create a token \n\u003e but will have to destroy to token immediately for security reasons.\n\u003e ```\n\n\n## 2. Create tables\nOk, now that you have a database created the next step is to create tables to work with. \n\n\u003e _General Methodology Note_: We'll work with a (rather simplified) \"chat application\" called **ChatSandra**:\n\u003e users, identified by a unique ID, write posts in several \"rooms\".\n\u003e Rooms are also uniquely identified by their name, such as `#gardening`. The design of our application is such\n\u003e that we need to be able to (a) retrieve all posts by a given user, sorted by descending date,\n\u003e and (b) retrieve all posts for a given room, sorted by descending date.\n\u003e As dictated by the best practices of data modeling with Cassandra, these requirements are satisfied by creating _two_ very similar tables (denormalization),\n\u003e as you'll see momentarily: they will contain the same posts, but stored (a.k.a. partitioned) in two different ways;\n\u003e and it will be our (that is, the application's) responsibility to maintain them aligned.\n\u003e Of course, we also need a `users` table - we will start with this one indeed.\n\n**✅ Step 2a. Navigate to the CQL Console and login to the database**\n\nIn the Summary screen for your database, select **_CQL Console_** from the top menu in the main window. This will take you to the CQL Console and automatically log you in.\n\n\u003cdetails\u003e\n    \u003csummary\u003eShow me! \u003c/summary\u003e\n    \u003cimg src=\"images/astra-cql-console.gif\" /\u003e\n\u003c/details\u003e\n\n\u003e _Note_: if you are working with your own Cassandra cluster (other than Astra DB), you will reach the CQL Console differently.\n\u003e Moreover, in that case you have to manually create the keyspace once in the CQL Console: this is done with a command similar to\n\u003e `CREATE KEYSPACE chatsandra WITH REPLICATION = {'class': 'NetworkTopologyStrategy', 'replication_factor': 3};`.\n\u003e See the Cassandra documentation for more details on this.\n\n**✅ Step 2b. Describe keyspaces and USE one of them**\n\nOk, now we're ready to rock. Creating tables is quite easy, but before we create one we need to tell the database which keyspace we are working with.\n\nFirst, let's **_DESCRIBE_** all of the keyspaces that are in the database. This will give us a list of the available keyspaces.\n\n📘 **Command to execute**\n```sql\nDESC KEYSPACES;\n```\n_\"desc\" is short for \"describe\", either is valid._\n\n\u003e CQL commands usually end with a semicolon `;`. If you hit Enter, nothing happens and you don't even get your prompt back, most likely it's because you have not closed the command with `;`. If in trouble, you can always get back to the prompt with `Ctrl-C` and start typing the command anew.\n\n📗 **Expected output**\n\n![Keyspaces in CQL](images/cql/01_desc_keyspaces.png)\n\nDepending on your setup you might see a different set of keyspaces than in the image. The one we care about for now is **_chatsandra_**. From here, execute the **_USE_** command with the **_chatsandra_** keyspace to tell the database our context is within **_chatsandra_**.\n\n\u003e Take advantage of the TAB-completion in the CQL Console. Try typing `use cha` and then pressing TAB, for example.\n\n📘 **Command to execute**\n```sql\nUSE chatsandra;\n```\n\n📗 **Expected output**\n\n![USE keyspace](images/cql/02_use_chatsandra.png)\n\nNotice how the prompt displays ```\u003cusername\u003e@cqlsh:chatsandra\u003e``` informing us we are **using** the **_chatsandra_** keyspace. Now we are ready to create our table.\n\n**✅ Step 2c. Create the users table**\n\nAt this point we can execute a command to create the **users** table.\nJust copy/paste the following command into your CQL console at the prompt.\nTry to identify the primary key, the partition key and the clustering columns\n(if any) for this table in the command:\n\n📘 **Command to execute**\n\n```sql\nCREATE TABLE IF NOT EXISTS users ( \n  email       TEXT,\n  name        TEXT,\n  password    TEXT,\n  user_id     UUID,\n  PRIMARY KEY (( email ))\n);\n```\n\nThen **_DESCRIBE_** your keyspace tables to ensure it is there.\n\n📘 **Command to execute**\n\n```sql\nDESC TABLES;\n```\n📗 **Expected output**\n\n![A table created](images/cql/03_user_table_created.png)\n\nAaaand **BOOM**, you created a table in your database. That's it.\nNow let's go ahead and create a couple more tables before we do\nsomething interesting with the data.\n\n**✅ Step 2d. Create the tables for posts**\n\nLet us create two more tables, which will contain the _posts_.\nAs remarked earlier, we will store the posts in two tables which\ndiffer in how they are partitioned: look at the commands below,\nthe differences mostly lie in the `PRIMARY KEY` specification:\n\n📘 **Command to execute**\n\n```sql\nCREATE TABLE IF NOT EXISTS posts_by_user ( \n  user_id     UUID, \n  post_id     TIMEUUID,\n  room_id     TEXT, \n  text        TEXT,\n  PRIMARY KEY ((user_id), post_id)\n) WITH CLUSTERING ORDER BY (post_id DESC);\n\nCREATE TABLE IF NOT EXISTS posts_by_room ( \n  room_id     TEXT, \n  post_id     TIMEUUID,\n  user_id     UUID,\n  text        TEXT,\n  PRIMARY KEY ((room_id), post_id)\n) WITH CLUSTERING ORDER BY (post_id DESC);\n```\n\nThen **_DESCRIBE_** your keyspace tables: you should see all three listed.\n\n📘 **Command to execute**\n\n```sql\nDESC TABLES;\n```\n\n📗 **Expected output**\n\n![A table created](images/cql/04_post_tables_created.png)\n\n_You may wonder, how did we arrive at this particular structure for the post tables?\nThe answer lies in the methodology for data modeling\nwith Cassandra, which, at its very core, states: **first look at the application's needs,\ndetermine the required workflows, then map them to a number of queries, finally design a table around each query**.\nWe create table **_posts_by_user_** to support a query such as \"get all posts by a user X\";\nthen we also need table **_posts_by_room_** for a query of type \"get all posts in room Y\".\nThe two tables have the same columns, but the different choice of partition key is what\nwill make the two queries possible on the respective tables._\n\n[🏠 Back to Table of Contents](#table-of-contents)\n\n## 3. Execute CRUD operations\nCRUD stands for \"**create, read, update, and delete**\". Simply put, they are the basic types of commands you need to work with ANY database in order to maintain data for your applications.\n\n**✅ Step 3a. (C)RUD = create = insert data, users**\n\nOur tables are in place so let's put some data in them. This is done with the **INSERT** statement. We'll start by inserting three rows into the **_users_** table.\n\n\u003e _Note_ that we have three users in this example: \"111...\", \"555...\" and \"999...\", which are having some pleasant conversations. In a real application, you would probably\n\u003e generate user IDs at the application level or with the `UUID()` primitive offered by CQL.\n\u003e See the [documentation](https://docs.datastax.com/en/cql-oss/3.3/cql/cql_reference/timeuuid_functions_r.html) for more details on time/uuid-related CQL functions.\n\nCopy and paste the following in your CQL Console:\n_(Once you have carefully examined the first of the following **INSERT** statements below, you can simply copy/paste the others which are very similar.)_\n\n📘 **Commands to execute**\n\n```sql\nINSERT INTO users (\n  email,    // TEXT\n  name,     // TEXT\n  password, // TEXT\n  user_id   // UUID: id of a user\n)\nVALUES (\n  'otzi@mail.com',\n  'Otzi Oney',\n  '123456',\n  11111111-1111-1111-1111-111111111111\n);\n\nINSERT INTO users (email, name, password, user_id) VALUES (\n  'fred@qmail.net', 'Fred Fivey', 'qwerty',\n  55555555-5555-5555-5555-555555555555\n);\nINSERT INTO users (email, name, password, user_id) VALUES (\n  'nina@zmail.org', 'Nina Niney', 's3cr3t',\n  99999999-9999-9999-9999-999999999999\n);\n```\n\n**✅ Step 3b. (C)RUD = create = insert data, posts**\n\nLet's run some more **INSERT** statements, this time for **posts**. We'll insert data into the **_posts_by_user_** table.\n_(Once you have carefully examined the first of the following **INSERT** statements below, you can simply copy/paste the others which are very similar.)_\n\n\u003e _Note_: in the following, we are using `TIMEUUID`s crafted by hand, to make things easier to visualize. In a real application, you would generate them at application\n\u003e level or, in some cases, using the `NOW()` primitive offered by CQL. In the values below, you can just pay attention to the first octet of hex digits.\n\n📘 **Commands to execute**\n\n```sql\n// Insert some data in the \"posts_by_user\" table\n\nINSERT INTO posts_by_user (\n  user_id,  // UUID: unique id for a user\n  post_id,  // TIMEUUID: unique uuid + timestamp\n  room_id,  // TEXT: id of a chat room\n  text      // TEXT: the post content itself\n)\nVALUES (\n  11111111-1111-1111-1111-111111111111,\n  22222222-5cff-11ec-be16-1fedb0dfd057,\n  '#hiking',\n  'I climbed Mt. Gumbo yesterday ...'\n);\n\nINSERT INTO posts_by_user (user_id, post_id, room_id, text) VALUES (\n  11111111-1111-1111-1111-111111111111,\n  77777777-5cff-11ec-be16-1fedb0dfd057,\n  '#running', 'Who likes marathons here?'\n);\nINSERT INTO posts_by_user (user_id, post_id, room_id, text) VALUES (\n  11111111-1111-1111-1111-111111111111,\n  aaaaaaaa-5cff-11ec-be16-1fedb0dfd057,\n  '#hiking', '... and Mt. Gumbo was easy!!!'\n);\nINSERT INTO posts_by_user (user_id, post_id, room_id, text) VALUES (\n  55555555-5555-5555-5555-555555555555,\n  bbbbbbbb-5cff-11ec-be16-1fedb0dfd057,\n  '#hiking', 'For us humans Gumbo is a tough one...!'\n);\nINSERT INTO posts_by_user (user_id, post_id, room_id, text) VALUES (\n  99999999-9999-9999-9999-999999999999,\n  cccccccc-5cff-11ec-be16-1fedb0dfd057,\n  '#running', 'I just love marathons.'\n);\nINSERT INTO posts_by_user (user_id, post_id, room_id, text) VALUES (\n  11111111-1111-1111-1111-111111111111,\n  eeeeeeee-5cff-11ec-be16-1fedb0dfd057,\n  '#running', 'Same here!'\n);\nINSERT INTO posts_by_user (user_id, post_id, room_id, text) VALUES (\n  55555555-5555-5555-5555-555555555555,\n  ffffffff-5cff-11ec-be16-1fedb0dfd057,\n  '#hiking', 'I have to buy new boots.'\n);\n```\n\nOk, we have a lovely bunch of posts in our chat application.\nBut **wait**: data is denormalized and the very same posts have to be inserted\nin table **_posts_by_room_** as well! Let's do it with the following command\n(please note that the `INSERT` statements are exactly the same as above,\nwith only the table name changed):\n\n📘 **Commands to execute**\n\n```sql\n// Insert some data in the \"posts_by_room\" table\n\nINSERT INTO posts_by_room (user_id, post_id, room_id, text) VALUES (\n  11111111-1111-1111-1111-111111111111,\n  22222222-5cff-11ec-be16-1fedb0dfd057,\n  '#hiking', 'I climbed Mt. Gumbo yesterday ...'\n);\n\nINSERT INTO posts_by_room (user_id, post_id, room_id, text) VALUES (\n  11111111-1111-1111-1111-111111111111,\n  77777777-5cff-11ec-be16-1fedb0dfd057,\n  '#running', 'Who likes marathons here?'\n);\nINSERT INTO posts_by_room (user_id, post_id, room_id, text) VALUES (\n  11111111-1111-1111-1111-111111111111,\n  aaaaaaaa-5cff-11ec-be16-1fedb0dfd057,\n  '#hiking', '... and Mt. Gumbo was easy!!!'\n);\nINSERT INTO posts_by_room (user_id, post_id, room_id, text) VALUES (\n  55555555-5555-5555-5555-555555555555,\n  bbbbbbbb-5cff-11ec-be16-1fedb0dfd057,\n  '#hiking', 'For us humans Gumbo is a tough one...!'\n);\nINSERT INTO posts_by_room (user_id, post_id, room_id, text) VALUES (\n  99999999-9999-9999-9999-999999999999,\n  cccccccc-5cff-11ec-be16-1fedb0dfd057,\n  '#running', 'I just love marathons.'\n);\nINSERT INTO posts_by_room (user_id, post_id, room_id, text) VALUES (\n  11111111-1111-1111-1111-111111111111,\n  eeeeeeee-5cff-11ec-be16-1fedb0dfd057,\n  '#running', 'Same here!'\n);\nINSERT INTO posts_by_room (user_id, post_id, room_id, text) VALUES (\n  55555555-5555-5555-5555-555555555555,\n  ffffffff-5cff-11ec-be16-1fedb0dfd057,\n  '#hiking', 'I have to buy new boots.'\n);\n```\n\n**✅ Step 3c. C(R)UD = read = read data**\n\nNow that we've inserted a set of rows (two sets, to be precise), let's take a look at how to read the data back out. This is done with a **SELECT** statement. In its simplest form we could just execute a statement like the following **_**cough_** **_**cough_**:\n```sql\n// Read all rows from \"posts_by_user\" table (careful with this ...)\nSELECT * FROM posts_by_user;\n```\n\nYou may have noticed my coughing fit a moment ago. Even though you can execute a **SELECT** statement with no partition key defined, this is NOT something you should do when using Apache Cassandra. We are doing it here for illustration purposes only and because our whole dataset is just a handful of values.\nGiven the data we inserted earlier, a more proper statement would be something like (while we are at it, we also explicitly specify which columns we want back):\n```sql\n// Read (some columns of) rows in a certain partition of \"posts_by_user\" table\nSELECT post_id, room_id, text FROM posts_by_user\n  WHERE user_id = 11111111-1111-1111-1111-111111111111;\n```\n\nThe key is to ensure we are **always selecting by some partition key** at a minimum, so to avoid the dreaded _full-cluster scans_ which yield performances that are generally unacceptable in production.\n\nOk, with that out of the way we can **READ** the data from the other table as well - remember we **INSERT**ed on both tables?\n\n📘 **Commands to execute**\n\n```sql\n// Read the whole \"posts_by_room\" table\n// (warning: not suitable for large tables in production)\nSELECT * FROM posts_by_room;\n\n// Read (some columns of) posts from a certain room (= a certain partition)\nSELECT user_id, text FROM posts_by_room WHERE room_id = '#hiking';\n```\n\n(again, in the second **SELECT** we specify some columns - it is something we may want to do in most cases).\n\n📗 **Expected output**\n\n![SELECT in CQL](images/cql/05_selects.png)\n\n_Notice how the two tables contain the same set of posts, but group them differently:\ntable `posts_by_user` is partitioned by user, while table `posts_by_room` is partitioned by room - and the corresponding outputs\nreflect this fact.\nThis is very much related to the fact that these two tables, in the data modeling process, were designed\nto answer two different questions, \"what are the posts by user X?\" and \"what are the posts in room Y?\" respectively.\nMoreover, within any partition in both tables, right as we required when creating the table,\nposts are kept (and displayed) sorted by decreasing `post_id` (which, due to the nature of `TIMEUUID`s,\nimplies a time-ordering as well)._\n\nOnce you execute the above **SELECT** statements you should see something like the expected output above. We have now **READ** the data we **INSERTED** earlier. Awesome job!\n\n_BTW, just a little extra for those who are interested. Since we used a [TIMEUUID](https://docs.datastax.com/en/cql-oss/3.3/cql/cql_reference/timeuuid_functions_r.html) type for our **post_id** field we can use the **dateOf()** function to determine the timestamp from the value. Check it out._\n\n```sql\n// Read all data from the posts_by_room table, \n// convert post_id into a timestamp, and label the column \"post_date\"\nSELECT user_id, dateOf(post_id) AS post_date, text FROM posts_by_room\n  WHERE room_id = '#hiking';\n```\n\n**✅ Step 3d. CR(U)D = update = update data**\n\nAt this point we've **_CREATED_** and **_READ_** some data, but what happens when you want to change some existing data to some new value? That's where **UPDATE** comes into play.\n_The use case is as follows: in our chat app, users are allowed to edit their previous posts._\n\nLet's take one of the records we created earlier and modify it. Recall that we **_INSERTED_** the following record in the **_posts_by_user_** table.\n```sql\n      // ** Just for reference: **\n      //  INSERT INTO posts_by_user (user_id, post_id, room_id, text) VALUES (\n      //    11111111-1111-1111-1111-111111111111,\n      //    aaaaaaaa-5cff-11ec-be16-1fedb0dfd057,\n      //    '#hiking', '... and Mt. Gumbo was easy!!!'\n      //  );\n```\n\nLet's also take a look at how the **_posts_by_user_** table was created. In order to **UPDATE** an existing record, indeed, we need to know the primary key we defined when we **CREATE**d the table.\n```sql\n      // ** Just for reference: **\n      // CREATE TABLE IF NOT EXISTS posts_by_user ( \n      //   user_id     UUID, \n      //   post_id     TIMEUUID,\n      //   room_id     TEXT, \n      //   text        TEXT,\n      //   PRIMARY KEY ((user_id), post_id)\n      // ) WITH CLUSTERING ORDER BY (post_id DESC);\n```\n\n\u003e Let's say that user \"111...\" has noticed the remark by \"555...\" and, perhaps a bit ashamed by their own boasting, wants to correct their assessment on the hike difficulty!\n\nLooking at ```PRIMARY KEY ((user_id), post_id)```, we know that both **user_id** and **post_id** are used to define uniqueness of the row.\nWe'll need both to update our record (plus, of course, some of the data columns, otherwise we are not changing anything in that row!).\n\n_You may remember that we used hardcoded values for **post_id** when we created these records (a real application would generate them live, one way or the other).\nImagine the UX for editing an existing post: when the user clicks the \"edit\" button, both **user_id** and **post_id** are known and can be provided to\nthe backend, where they ultimately become part of an **UPDATE** statement._\n\nSo we can run the following **UPDATE** statement and help user \"111...\" fix their post on table **_posts_by_user_**\n(we also subsequently read back the data as a check):\n\n📘 **Commands to execute**\n\n```sql\nUPDATE posts_by_user \n  SET text = '... and Mt. Gumbo was NOT SO easy!!!' \n    WHERE user_id = 11111111-1111-1111-1111-111111111111\n    AND   post_id = aaaaaaaa-5cff-11ec-be16-1fedb0dfd057;\n\nSELECT post_id, room_id, text FROM posts_by_user\n  WHERE user_id = 11111111-1111-1111-1111-111111111111;\n```\n\n📗 **Expected output**\n\n![Updating in CQL](images/cql/06_updated.png)\n\nBut **wait**: data, again, is denormalized! This means that we have to make sure\nsuch an edit is performed on table **_posts_by_room_** as well.\nSince the primary key of that table is given as `PRIMARY KEY ((room_id), post_id)`,\nthese are the fields to provide, along with `text` itself, to the **UPDATE** statement.\n\nAnd we _could_ run an **UPDATE**. But, lo and behold, in Cassandra **UPDATE**s\nand **INSERT**s are (almost) the same, as a consequence of its architecture and\nthe way storage and write logic are structured. We can then update the row with\nan **INSERT** statement like the following (note that we provide: primary key +\nany field that we want to modify; and leave out the other, unchanged fields):\n\n📘 **Commands to execute**\n```sql\nINSERT INTO posts_by_room (room_id, post_id, text) VALUES (\n  '#hiking',\n  aaaaaaaa-5cff-11ec-be16-1fedb0dfd057,\n  '... and Mt. Gumbo was NOT SO easy!!!'\n);\n\nSELECT post_id, user_id, text FROM posts_by_room WHERE room_id = '#hiking';\n```\n\nThat's it, we successfully edited a post (on both tables).\nAll that's left now is to **DELETE** some data.\n\n**✅ Step 3e. CRU(D) = delete = remove data**\n\nThe final operation from our **CRUD** acronym is **DELETE**. This is the operation we use when we want to remove data from the database.\nIn Apache Cassandra you can **DELETE** from the cell level all the way up to the partition\n_(meaning I could remove a single column in a single row or I could remove a whole partition)_ using the same **DELETE** command.\n\n_Generally speaking, it's best to perform as few delete operations as possible on the largest amount of data. Think of it this way, if you want to delete ALL data in a table, don't delete each individual cell, just **TRUNCATE** the table. If you need to delete all the rows in a partition, don't delete each row, **DELETE** the partition, and so on._\n\n\u003e User \"555...\" notices the post by \"111...\" being edited and wants to remove their snarky remark. Let's help them!\n\nWhen deleting a row on a given table, we have to specify the values of the primary key for that table. And don't forget\nthat, in our data model, a post appears as two separate rows in the two tables, so we have to perform\ntwo different **DELETE** operations!\n\n📘 **Commands to execute**\n\n```sql\nSELECT post_id, room_id, text FROM posts_by_user\n  WHERE user_id = 55555555-5555-5555-5555-555555555555;\n\nSELECT post_id, user_id, text FROM posts_by_room WHERE room_id = '#hiking';\n\nDELETE FROM posts_by_user\n  WHERE user_id = 55555555-5555-5555-5555-555555555555\n  AND post_id = bbbbbbbb-5cff-11ec-be16-1fedb0dfd057;\n\nDELETE FROM posts_by_room\n  WHERE room_id = '#hiking'\n  AND post_id = bbbbbbbb-5cff-11ec-be16-1fedb0dfd057;\n\nSELECT post_id, room_id, text FROM posts_by_user\n  WHERE user_id = 55555555-5555-5555-5555-555555555555;\n\nSELECT post_id, user_id, text FROM posts_by_room WHERE room_id = '#hiking';\n```\n\n(Notice in the above, for your convenience, we read the tables, then delete the rows, then read them again).\n\n📗 **Expected output**\n\n![Deleting in CQL](images/cql/07_deleting.png)\n\nNotice the rows are now removed from both tables: it is as simple as that.\n\n### Homework note\n\nTo submit the **homework**, please take a screenshot of the CQL Console showing the rows in tables\n`posts_by_user` and `posts_by_room` before _and_ after executing the DELETE statements.\n\n## 4. Wrapping up\nWe've just scratched the surface of what you can do using Astra DB, built on Apache Cassandra.\nGo take a look at [DataStax for Developers](https://www.datastax.com/dev) to see what else is possible.\nThere's plenty to dig into!\n\n# Done?\n\nCongratulations: you made to the end of today's workshop.\n\nDon't forget to [submit your homework](https://dtsx.io/homework-intro-to-cassandra) and be awarded a nice verified badge!\n\n![Badge](images/badge/intro-to-cassandra.png)\n\n**... and see you at our next workshop!**\n\n\u003e Sincerely yours, The DataStax Developers\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdatastaxdevs%2Fworkshop-intro-to-cassandra","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdatastaxdevs%2Fworkshop-intro-to-cassandra","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdatastaxdevs%2Fworkshop-intro-to-cassandra/lists"}