I had given up on SQLite 5 years ago, because of some false myths across the internet. Thank you Aaron for bringing me back to this powerful database! Shipped almost two apps, love the simplicity and the high performance of SQLite! This course is best possible investment 🚀Iliyan Slavov
Build with and integrate SQLite into your production applications.
I've made my test data available for you to use and follow along.
(keyboard keys clacking) The last JSON function that we're gonna look at here is json_each. And this is in that third category of JSON functions. We had scalar aggregate, and this is the table function part. And so I have a table here, it's just a very basic table, select * from products. And you'll see we have a few products, name, price, and then we have tags as a JSON array. And what a JSON table function does, where a scalar function returns a single value and aggregate works over multiple values, but returns a single value, a table function, like we've talked about, returns multiple rows and potentially, multiple columns. So it can be very, very interesting. So in this case, what we're gonna do is we're going to pretend that this tags column is its own table and we wanna kind of join 'em together and get multiple rows, one per tag, right? I don't know that this schema is very good, this storing tags as a JSON array. That being said, I don't know if it's very bad, I can see a use case or a situation where this is a great schema and you don't wanna create another table and have a multi-multi relationship going on. I'm not here to judge this particular schema, but if you find yourself with a schema like this and you need to blow it up into a proper result set where every tag has its own row, well, here we go. So what we're gonna do here is we're gonna say select * from products. We already did that. We're gonna do an implicit cross join here by just putting a comma. So we'll start with the implicit cross join, and then I will show you the explicit cross join. And this is a little bit unique for json_each, but we're gonna do json_each(products.tags) and if we run that, whoa, we get a ton of stuff. And so you'll see all kinds of stuff here. If we were to drop that off, you'll see we've got id, name, price, and tags. That is the products table. That's it. So that's one, two, three, four columns in the products table. And if we add that back on this json_each, you'll see it explodes. And so we have the first four columns here, which belong to the products table, but then we start to get some interesting stuff. We get key and value, type. Atom? Hmm, interesting. Id, parent, fullkey and path. So you'll recognize much of this over here. This is the path to get to this particular value. Now, the value and the atom, it's a little bit goofy in my opinion. Don't tell the creators. This is a little bit goofy. I'm sure it's very, very useful. But in this case, value and atom are identical. However, if the value, so if we were to come over here, let's just drop this off for a second. And we were to make this last one an array instead of a string, and now we were to run this again, you'll see that the atom is different. So when the value is a SQL primitive, so text, all of these are texts and then, whoops, there's an array, then the atom goes null. So the atom is the SQL primitive, unless it's not representable as a SQL primitive, in which case, it goes null. And the value is always going to be populated unless the value is actually null. But the value's always going to be populated. Here we have the SQL primitive, which is a string of electronics, and here we have the JSON representation of an array. And so you also get the type next to it so you can see what type it is, if that is useful. I'm going to undo this and turn that back into a string. Yep, that is what I was afraid of. I am going to undo this and turn this back into a string and then we can run this again and we're back in a good spot. So normally, what you would do is you would do something like, let's say value. So we'll do id, name and value. We'll do products.id, name and value. And that gives us the tags, the value of the actual tags. I think we can say as tags here. And if we were to do tags.value, that maybe feels a little bit cleaner if we were to turn back on everything again. You'll see we still have all of the columns there. Now, value as a name, value could be anything. It's like calling something data. So I don't super love that. So we're gonna go back here and instead of saying this as tags, we're just gonna leave that alone. And we're gonna say value as singular tag. That looks a little bit better to me. Now, this implicit cross join thing. So what we've actually done here is we've done cross join json_each. Now, this is a little bit different than a normal cross join because these will be associated with the row from wence they came. Did you follow that? In a traditional cross join, everything is joined together. The whole thing is cross joined together. With a json_each however, it's only cross joined for the rows to which it applies, the rows where the data actually came from. You can also just do a join and that is the same thing. So all three of those are the exact same. And this json_each is going to inspect that JSON and turn it into a result set and then cross join it with the row that it came from, not the entire left-hand table. So this is pretty nice. Now, what is the difference between json_each and json_tree? Well, they are slightly different. Let's go ahead and we'll select star. You'll see that they are slightly different. So we get the top level and then we get each of the individual items. And so if we were to compare that again with json_each, you'll see we just get the individual items. So the real difference here is json_each is a shallow iterator and json_tree is a recursive iterator. So if you have deeply nested JSON, you need to go in, honestly like a tree structure, which is maybe where they got the name, if you need to go in recursively and populate rows based on a recursive JSON structure, json_tree is what you're looking for. In our case, we just wanted first level nesting. We just had a shallow array and so this worked perfectly. Now, it's not just arrays. You can json_each over an object. You can json_tree over an object. And so depending on your use case will depend on which one you reach for. But just remember that json_each is shallow and json_tree is recursive. So those are the two table value JSON functions. So you've got scalar, aggregate, and table. These are the table. And it can be very, very helpful to turn some JSON back into a proper table and then work your SQL magic on that instead of mucking around with the JSON.