{"id":1753,"date":"2021-03-21T14:20:12","date_gmt":"2021-03-21T13:20:12","guid":{"rendered":"https:\/\/benjiweber.co.uk\/blog\/?p=1753"},"modified":"2021-03-21T14:20:15","modified_gmt":"2021-03-21T13:20:15","slug":"thinking-in-questions-with-sql","status":"publish","type":"post","link":"https:\/\/benjiweber.co.uk\/blog\/2021\/03\/21\/thinking-in-questions-with-sql\/","title":{"rendered":"Thinking in Questions with SQL"},"content":{"rendered":"\n<p class=\"lead\">I love SQL, despite its many flaws.<\/p>\n\n\n\n<style type=\"text\/css\">span.term {font-family: monospace; } h2 { padding-top: 20px; }<\/style>\n\n\n\n<p>Much is argued about functional programming vs object oriented. Different ways of instructing computers.<\/p>\n\n\n\n<p>SQL is different. SQL is a language where I can ask the computer a question and it will figure out how to answer it for me. <\/p>\n\n\n\n<p>Fluency in SQL is a very practical skill. It will make your life easier day to day. It&#8217;s not perfect, it has many flaws (like null) but it is in widespread use (unlike, say, <a href=\"https:\/\/en.wikipedia.org\/wiki\/Prolog\">prolog<\/a> or <a href=\"https:\/\/en.wikipedia.org\/wiki\/D_(data_language_specification)\">D<\/a>). <\/p>\n\n\n\n<h2>Useful in lots of contexts<\/h2>\n\n\n\n<p>As an engineer, sql databases often save me writing lots of code to transform data. They save me worrying about the best way to manage finite resources like memory. I write the question and the database (usually) figures out the most efficient algorithm to use, given the shape of the data right now, and the resources available to process it. Like magic.<\/p>\n\n\n\n<p>SQL helps me think about data in different ways, lets me focus on the questions I want to ask of the data; independent of the best way to store and structure data.<\/p>\n\n\n\n<figure class=\"wp-block-embed-twitter wp-block-embed is-type-rich is-provider-twitter\"><div class=\"wp-block-embed__wrapper\">\n<blockquote class=\"twitter-tweet\" data-width=\"550\" data-dnt=\"true\"><p lang=\"en\" dir=\"ltr\">One of my favourite things with Postgres is being able to think about read\/write models separately. <br><br>1) Lazily dump data in as json. <br>2) Build a read model with [materialized] views<br>3) Refactor the write model underneath<br><br>Get the convenience of sql for reads before (3) <a href=\"https:\/\/t.co\/31xhuEK7ty\">pic.twitter.com\/31xhuEK7ty<\/a><\/p>&mdash; Benji Weber (@benjiweber) <a href=\"https:\/\/twitter.com\/benjiweber\/status\/1373362733467455489?ref_src=twsrc%5Etfw\">March 20, 2021<\/a><\/blockquote><script async src=\"https:\/\/platform.twitter.com\/widgets.js\" charset=\"utf-8\"><\/script>\n<\/div><\/figure>\n\n\n\n<p>As a manager, I often want to measure things, to know the answer to questions. SQL lets me ask lots of questions of computers directly without having to bother people. I can explore my ideas with a shorter feedback loop than if I could only pose questions to my team.<\/p>\n\n\n\n<p>SQL is a language for expressing our questions in a way that machines can help answer them; useful in so many contexts.<\/p>\n\n\n\n<p>It would be grand if even more things spoke SQL. Imagine you could ask questions in a shell instead of having to teach it how to transform data\u00a0 <\/p>\n\n\n\n<figure class=\"wp-block-embed-twitter wp-block-embed is-type-rich is-provider-twitter\"><div class=\"wp-block-embed__wrapper\">\n<blockquote class=\"twitter-tweet\" data-width=\"550\" data-dnt=\"true\"><p lang=\"en\" dir=\"ltr\">Playing with using sqlite to query stdout from shell commands <a href=\"https:\/\/t.co\/DWZPYGemYA\">pic.twitter.com\/DWZPYGemYA<\/a><\/p>&mdash; Benji Weber (@benjiweber) <a href=\"https:\/\/twitter.com\/benjiweber\/status\/1343971130474954752?ref_src=twsrc%5Etfw\">December 29, 2020<\/a><\/blockquote><script async src=\"https:\/\/platform.twitter.com\/widgets.js\" charset=\"utf-8\"><\/script>\n<\/div><\/figure>\n\n\n\n<h2>Why do we avoid it?<\/h2>\n\n\n\n<p>SQL is terrific. So why is there so much effort expended in avoiding it? We learn ORM abstractions on top of it. We treat SQL databases as glorified buckets of data: chuck data in, pull data out.<\/p>\n\n\n\n<p>Transforming data in application code gives a comforting amount of control over the process, but is often harder and slower than asking the right question of the database in the first place.<\/p>\n\n\n\n<p>Do you see SQL as a language for storing and retrieving bits of data, or as a language for expressing questions?<\/p>\n\n\n\n<h2>Let go of control&nbsp;<\/h2>\n\n\n\n<p>The database can often figure out the best way of answering the question better than you.<\/p>\n\n\n\n<p>Let&#8217;s take an identical query with three different states of data. <\/p>\n\n\n\n<p>Here&#8217;s two simple relations with 1 attribute each. a and b. With a single tuple in each relation.\u00a0<\/p>\n\n\n\n<pre lang=\"sql\">create table a(id int);\ncreate table b(id int);\ninsert into a values(1);\ninsert into b values(1);\nexplain analyze select * from a natural join b;<\/pre>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/benjiweber.co.uk\/blogpgb1.png\"><img src=\"https:\/\/benjiweber.co.uk\/blogpgb1.png\" alt=\"\"\/><\/a><\/figure>\n\n\n\n<p>&#8220;explain analyze&#8221; is telling us <em>how<\/em> postgres is going to answer our question. The operations it will take, and how expensive they are. We haven&#8217;t told it to use quicksort, it has elected to do so.<\/p>\n\n\n\n<p>Looking at how the database is doing things is interesting, but let&#8217;s make it more interesting by changing the data. Let&#8217;s add in a boatload more values and re-run the same query.<\/p>\n\n\n\n<pre lang=\"sql\">insert into a select * from generate_series(1,10000000);\nexplain analyze select * from a natural join b;<\/pre>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/benjiweber.co.uk\/blogpgb2.png\"><img src=\"https:\/\/benjiweber.co.uk\/blogpgb2.png\" alt=\"\"\/><\/a><\/figure>\n\n\n\n<p>We&#8217;ve used <a href=\"https:\/\/www.postgresql.org\/docs\/current\/functions-srf.html\">generate_series<\/a> to generate ten million tuples in relation &#8216;a&#8217;. Note the &#8220;Sort method&#8221; has changed to use disk because the data set is larger compared to the resources the database has available. I haven&#8217;t had to tell it to do this. I just asked the same question and it has figured out that it needs to use a different method to answer the question now that the data has changed.<\/p>\n\n\n\n<p>But actually we&#8217;ve done the database a disservice here by running the query immediately after inserting our data. It&#8217;s not had a chance to catch up yet. Let&#8217;s give it a chance by running <a href=\"https:\/\/www.postgresql.org\/docs\/9.1\/sql-analyze.html\">analyze<\/a> on our relations to force an update to its knowledge of the shape of our data.\u00a0<\/p>\n\n\n\n<pre lang=\"sql\">analyze a;\nanalyze b;\nexplain analyze select * from a natural join b;<\/pre>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/benjiweber.co.uk\/blogpgb3.png\"><img src=\"https:\/\/benjiweber.co.uk\/blogpgb3.png\" alt=\"\"\/><\/a><\/figure>\n\n\n\n<p>Now re-running the same query is a lot faster, and the approach has significantly changed. It&#8217;s now using a <span class=\"term\">Hash Join<\/span> not a <span class=\"term\">Merge Join<\/span>. It has also introduced parallelism to the query execution plan. It&#8217;s an order of magnitude faster. Again I haven&#8217;t had to tell the database to do this, it has figured out an easier way of answering the question now that it knows more about the data.<\/p>\n\n\n\n<h2>Asking Questions<\/h2>\n\n\n\n<p>Let&#8217;s look at some of the building blocks SQL gives us for expressing questions. The simplest building block we have is asking for literal values.<\/p>\n\n\n\n<pre lang=\"sql\">SELECT 'Eddard';\nSELECT 'Catelyn';<\/pre>\n\n\n\n<p>A value without a name is not very useful. Let&#8217;s rename them.<\/p>\n\n\n\n<pre lang=\"sql\">SELECT 'Eddard' AS forename;\nSELECT 'Catelyn' AS forename;<\/pre>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/benjiweber.co.uk\/blogpgc1.png\"><img src=\"https:\/\/benjiweber.co.uk\/blogpgc1.png\" alt=\"\"\/><\/a><\/figure>\n\n\n\n<p>What if we wanted to ask a question of multiple Starks: Eddard OR Catelyn OR Bran? That&#8217;s where <span class=\"term\">UNION<\/span> comes in.&nbsp;<\/p>\n\n\n\n<pre lang=\"sql\">select 'Eddard' as forename \nUNION select 'Catelyn' AS forename \nUNION SELECT 'Bran' AS forename;<\/pre>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/benjiweber.co.uk\/blogpgc2.png\"><img src=\"https:\/\/benjiweber.co.uk\/blogpgc2.png\" alt=\"\"\/><\/a><\/figure>\n\n\n\n<p>We can also express things like someone leaving the family. With <span class=\"term\">EXCEPT<\/span>.<\/p>\n\n\n\n<pre lang=\"sql\">select 'Eddard' as forename \nUNION select 'Catelyn' AS forename \nUNION select 'Bran' AS forename \nEXCEPT select 'Eddard' as forename;<\/pre>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/benjiweber.co.uk\/blogpgc3.png\"><img src=\"https:\/\/benjiweber.co.uk\/blogpgc3.png\" alt=\"\"\/><\/a><\/figure>\n\n\n\n<p>What about people joining the family? How can we see who&#8217;s in both families. That&#8217;s where <span class=\"term\">INTERSECT<\/span> comes in.<\/p>\n\n\n\n<pre lang=\"sql\">(\n  SELECT 'Jamie' AS forename \n  UNION select 'Cersei' AS forename \n  UNION select 'Sansa' AS forename\n) \nINTERSECT \n(\n  select 'Sansa' AS forename\n);<\/pre>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/benjiweber.co.uk\/blogpgc4.png\"><img src=\"https:\/\/benjiweber.co.uk\/blogpgc4.png\" alt=\"\"\/><\/a><\/figure>\n\n\n\n<p>It&#8217;s getting quite tedious having to type out every value in every query already.&nbsp;<\/p>\n\n\n\n<p>SQL uses the metaphor &#8220;table&#8221;. We have tables of data. To me that gives connotations of spreadsheets. Postgres uses the term &#8220;relation&#8221; which I think is more helpful. Each &#8220;relation&#8221; is a collection of data which have some relation to each other. Data for which a predicate is true.&nbsp;<\/p>\n\n\n\n<p>Let&#8217;s store the starks together. They are related to each other.&nbsp;<\/p>\n\n\n\n<pre lang=\"sql\">create table stark as \nSELECT 'Sansa' as forename  \nUNION select 'Eddard' AS forename  \nUNION select 'Catelyn' AS forename  \nUNION select 'Bran' AS forename ;\n\ncreate table lannister as \nSELECT 'Jamie' AS forename \nUNION select 'Cersei' AS forename \nUNION select 'Sansa' AS forename;<\/pre>\n\n\n\n<p>Now we have stored relations of related data that we can ask questions of. We&#8217;ve stored the facts&nbsp;where &#8220;is a member of house stark&#8221; and &#8220;is a member of house lannister&#8221; are true. What if we want people who are in both houses. A relational AND. That&#8217;s where <span class=\"term\"><a href=\"https:\/\/en.wikipedia.org\/wiki\/Relational_algebra#Natural_join\">NATURAL JOIN<\/a><\/span> comes in.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/benjiweber.co.uk\/blogpgd2.png\"><img src=\"https:\/\/benjiweber.co.uk\/blogpgd2.png\" alt=\"\"\/><\/a><\/figure>\n\n\n\n<p><span class=\"term\">NATURAL JOIN<\/span> is not quite the same as the set based and (<span class=\"term\">INTERSECT<\/span> above). <span class=\"term\">NATURAL JOIN<\/span> will work even if there are different arity tuples in the two relations we are comparing.<\/p>\n\n\n\n<p>Let&#8217;s illustrate this by creating a relation pet with two attributes.<\/p>\n\n\n\n<p>create table pet as&nbsp;<\/p>\n\n\n\n<pre lang=\"sql\">CREATE TABLE pet as \nSELECT 'Sansa' as forename, 'Lady' as pet\nUNION select 'Bran' AS forename, 'Summer' as pet;<\/pre>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/benjiweber.co.uk\/blogpge1.png\"><img src=\"https:\/\/benjiweber.co.uk\/blogpge1.png\" alt=\"\"\/><\/a><\/figure>\n\n\n\n<p>Now we have an AND, what about OR? We have a set-or above (UNION). I think the closest thing to a relational OR is a full outer join.&nbsp;<\/p>\n\n\n\n<pre lang=\"sql\">create table animal as select 'Lady' as forename, 'Wolf' as species UNION select 'Summer' as forename, 'Wolf' as species;\nselect * from stark full outer join animal using(forename);<\/pre>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/benjiweber.co.uk\/blogpge2.png\"><img src=\"https:\/\/benjiweber.co.uk\/blogpge2.png\" alt=\"\"\/><\/a><\/figure>\n\n\n\n<p>Ok so we can ask simple questions with ands and ors. There are also equivalents of most of the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Relational_algebra#Joins_and_join-like_operators\">relational algebra operations<\/a>.\u00a0<\/p>\n\n\n\n<h2>What if I want to invade King&#8217;s Landing?<\/h2>\n\n\n\n<p>What about more interesting questions? We can do those too. Let&#8217;s jump ahead a bit. <\/p>\n\n\n\n<p>What if we&#8217;re wanting to plan an attack on Kings Landing and need to consider the routes we could take to get there. Starting from just some facts about the travel options between locations, let&#8217;s ask the database to figure out routes for us.<\/p>\n\n\n\n<p>First the data.&nbsp;<\/p>\n\n\n\n<pre lang=\"sql\">create table move (place text, method text, newplace text);\ninsert into move(place,method,newplace) values\n('Winterfell','Horse','Castle Black'),\n('Winterfell','Horse','White Harbour'),\n('Winterfell','Horse','Moat Cailin'),\n('White Harbour','Ship','King''s Landing'),\n('Moat Cailin','Horse','Crossroads Inn'),\n('Crossroads Inn','Horse','King''s Landing');<\/pre>\n\n\n\n<p>Now let&#8217;s figure out a query that will let us plan routes between origin and destination as below<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/benjiweber.co.uk\/blogpgf2.png\"><img src=\"https:\/\/benjiweber.co.uk\/blogpgf2.png\" alt=\"\"\/><\/a><\/figure>\n\n\n\n<p>We don&#8217;t need to store any intermediate data, we can ask the question all in one go. Here &#8220;route_planner&#8221; is a view (a saved question)<\/p>\n\n\n\n<pre lang=\"sql\">create view route_planner as\nwith recursive route(place, newplace, method, length, path) as (\n\tselect place, newplace, method, 1 as length, place as path from move --starting point\n\t\tunion -- or \n\tselect -- next step on journey\n\t\troute.place, \n\t\tmove.newplace, \n\t\tmove.method, \n\t\troute.length + 1, -- extra step on the found route \n\t\tpath || '-[' || route.method || ']->' || move.place as path -- describe the route\n\tfrom move \n\tjoin route ON route.newplace = move.place -- restrict to only reachable destinations from existing route\n) \nSELECT \n\tplace as origin, \n\tnewplace as destination, \n\tlength, \n\tpath || '-[' || method ||  ']->' || newplace as instructions \nFROM route;\n<\/pre>\n\n\n\n<p>I know this is a bit &#8220;<a href=\"https:\/\/pbs.twimg.com\/media\/Bs13i6LCcAAvwCf?format=jpg&amp;name=small\">rest of the owl<\/a>&#8221; compared to what we were doing above.  I hope\u00a0it at least illustrates the extent of what is possible. (It&#8217;s based on the <a href=\"https:\/\/www.doc.gold.ac.uk\/~mas02gw\/prolog_tutorial\/prologpages\/recursion.html\">prolog tutorial<\/a>). We have started from some facts about adjacent places and asked the database to figure out routes for us.<\/p>\n\n\n\n<p>Let&#8217;s talk it through\u2026<\/p>\n\n\n\n<pre lang=\"sql\">create view route_planner as<\/pre>\n\n\n\n<p>this saves the relation that&#8217;s the result of the given query with a name. We did this above with<\/p>\n\n\n\n<pre lang=\"sql\">create table lannister as \nSELECT 'Jamie' AS forename \nUNION select 'Cersei' AS forename \nUNION select 'Sansa' AS forename;<\/pre>\n\n\n\n<p>While create table will store a static dataset, a view will re-execute the query each time we interrogate it. It&#8217;s always fresh even if the underlying facts change.<\/p>\n\n\n\n<pre lang=\"sql\">with recursive route(place, newplace, method, length, path) as (...);<\/pre>\n\n\n\n<p>This creates a named portion of the query, called a &#8220;<a href=\"https:\/\/www.postgresql.org\/docs\/current\/queries-with.html\">common table expression<\/a>&#8220;. You could think of it like an extract-method refactoring.\u00a0 We&#8217;re giving part of the query a name to make it easier to understand. This also allows us to make it recursive, so we can build answers on top of partial answers, in order to build up our route.<\/p>\n\n\n\n<pre lang=\"sql\">select place, newplace, method, 1 as length, place as path from move<\/pre>\n\n\n\n<p>This gives us all the possible starting points on our journeys. Every place we know we can make a move from.&nbsp;<\/p>\n\n\n\n<p>We can think of two steps of a journey as the first step OR the second step. So we represent this OR with a <span class=\"term\">UNION<\/span>.&nbsp;<\/p>\n\n\n\n<pre lang=\"sql\">join route ON route.newplace = move.place<\/pre>\n\n\n\n<p>Once we&#8217;ve found our first and second steps, the third step is just the same\u2014treating the second step as the starting point. &#8220;route&#8221; here is the partial journey so far, and we look for feasible connected steps.\u00a0<\/p>\n\n\n\n<pre lang=\"sql\">path || '-[' || route.method || ']->' || move.place as path;<\/pre>\n\n\n\n<p>here we concatenate instructions so far through the journey. Take the path travelled so far, and append the next mode of transport and next destination.<\/p>\n\n\n\n<p>Finally we select the completed journey from our complete route<\/p>\n\n\n\n<pre lang=\"sql\">SELECT \n\tplace as origin, \n\tnewplace as destination, \n\tlength, \n\tpath || '-[' || method ||  ']->' || newplace as instructions \nFROM route;<\/pre>\n\n\n\n<p>Then we can ask the question<\/p>\n\n\n\n<pre lang=\"sql\">select instructions from route_planner \nwhere origin = 'Winterfell' \nand destination = 'King''s Landing';<\/pre>\n\n\n\n<p>and get the answer<\/p>\n\n\n\n<pre>                                 instructions                                   \n-------------------------------------------------------------------------------\nWinterfell-[Horse]->White Harbour-[Ship]->King's Landing\nWinterfell-[Horse]->Moat Cailin-[Horse]->Crossroads Inn-[Horse]->King's Landing\n(2 rows)\n<\/pre>\n\n\n\n<h2>Thinking in Questions<\/h2>\n\n\n\n<p>Learning SQL well can be a worthwhile investment of time. It&#8217;s a language in widespread use, across many underlying technologies.\u00a0<\/p>\n\n\n\n<p>Get the most out of it by shifting your thinking from &#8220;how can I get at my data so I can answer questions&#8221; to &#8220;How can I express my question in this language?&#8221;.\u00a0<\/p>\n\n\n\n<p>Let the database figure out how to best answer the question. It knows most about the data available and the resources at hand.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I love SQL, despite its many flaws. Much is argued about functional programming vs object oriented. Different ways of instructing computers. SQL is different. SQL is a language where I can ask the computer a question and it will figure out how to answer it for me. Fluency in SQL is a very practical skill&#8230;.  <a href=\"https:\/\/benjiweber.co.uk\/blog\/2021\/03\/21\/thinking-in-questions-with-sql\/\" class=\"more-link\" title=\"Read Thinking in Questions with SQL\">Read more &raquo;<\/a><\/p>\n","protected":false},"author":2,"featured_media":1789,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[31],"tags":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v14.9 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<meta name=\"description\" content=\"Get the most out of SQL by shifting from &quot;how can I get at my data so I can answer questions&quot; to &quot;How can I express my question in this language?&quot;\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/benjiweber.co.uk\/blog\/2021\/03\/21\/thinking-in-questions-with-sql\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Thinking in Questions with SQL - Benji&#039;s Blog\" \/>\n<meta property=\"og:description\" content=\"Get the most out of SQL by shifting from &quot;how can I get at my data so I can answer questions&quot; to &quot;How can I express my question in this language?&quot;\" \/>\n<meta property=\"og:url\" content=\"https:\/\/benjiweber.co.uk\/blog\/2021\/03\/21\/thinking-in-questions-with-sql\/\" \/>\n<meta property=\"og:site_name\" content=\"Benji&#039;s Blog\" \/>\n<meta property=\"article:published_time\" content=\"2021-03-21T13:20:12+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2021-03-21T13:20:15+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/benjiweber.co.uk\/blog\/wp-content\/uploads\/2021\/03\/blogpgf2.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1320\" \/>\n\t<meta property=\"og:image:height\" content=\"481\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebSite\",\"@id\":\"https:\/\/benjiweber.co.uk\/blog\/#website\",\"url\":\"https:\/\/benjiweber.co.uk\/blog\/\",\"name\":\"Benji&#039;s Blog\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":\"https:\/\/benjiweber.co.uk\/blog\/?s={search_term_string}\",\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"ImageObject\",\"@id\":\"https:\/\/benjiweber.co.uk\/blog\/2021\/03\/21\/thinking-in-questions-with-sql\/#primaryimage\",\"inLanguage\":\"en-US\",\"url\":\"https:\/\/benjiweber.co.uk\/blog\/wp-content\/uploads\/2021\/03\/blogpgf2.png\",\"width\":1320,\"height\":481},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/benjiweber.co.uk\/blog\/2021\/03\/21\/thinking-in-questions-with-sql\/#webpage\",\"url\":\"https:\/\/benjiweber.co.uk\/blog\/2021\/03\/21\/thinking-in-questions-with-sql\/\",\"name\":\"Thinking in Questions with SQL - Benji&#039;s Blog\",\"isPartOf\":{\"@id\":\"https:\/\/benjiweber.co.uk\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/benjiweber.co.uk\/blog\/2021\/03\/21\/thinking-in-questions-with-sql\/#primaryimage\"},\"datePublished\":\"2021-03-21T13:20:12+00:00\",\"dateModified\":\"2021-03-21T13:20:15+00:00\",\"author\":{\"@id\":\"https:\/\/benjiweber.co.uk\/blog\/#\/schema\/person\/45ecb36b51f4ce99e6929d2d31ca5c09\"},\"description\":\"Get the most out of SQL by shifting from \\\"how can I get at my data so I can answer questions\\\" to \\\"How can I express my question in this language?\\\"\",\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/benjiweber.co.uk\/blog\/2021\/03\/21\/thinking-in-questions-with-sql\/\"]}]},{\"@type\":\"Person\",\"@id\":\"https:\/\/benjiweber.co.uk\/blog\/#\/schema\/person\/45ecb36b51f4ce99e6929d2d31ca5c09\",\"name\":\"benji\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\/\/benjiweber.co.uk\/blog\/#personlogo\",\"inLanguage\":\"en-US\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/05fb47b31a0b329e1b790074a9b624ef?s=96&d=mm&r=g\",\"caption\":\"benji\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","amp_enabled":true,"_links":{"self":[{"href":"https:\/\/benjiweber.co.uk\/blog\/wp-json\/wp\/v2\/posts\/1753"}],"collection":[{"href":"https:\/\/benjiweber.co.uk\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/benjiweber.co.uk\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/benjiweber.co.uk\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/benjiweber.co.uk\/blog\/wp-json\/wp\/v2\/comments?post=1753"}],"version-history":[{"count":70,"href":"https:\/\/benjiweber.co.uk\/blog\/wp-json\/wp\/v2\/posts\/1753\/revisions"}],"predecessor-version":[{"id":1827,"href":"https:\/\/benjiweber.co.uk\/blog\/wp-json\/wp\/v2\/posts\/1753\/revisions\/1827"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/benjiweber.co.uk\/blog\/wp-json\/wp\/v2\/media\/1789"}],"wp:attachment":[{"href":"https:\/\/benjiweber.co.uk\/blog\/wp-json\/wp\/v2\/media?parent=1753"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/benjiweber.co.uk\/blog\/wp-json\/wp\/v2\/categories?post=1753"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/benjiweber.co.uk\/blog\/wp-json\/wp\/v2\/tags?post=1753"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}