{"id":14947,"date":"2023-07-27T00:55:56","date_gmt":"2023-07-27T07:55:56","guid":{"rendered":"https:\/\/tdengine.com\/?p=14947"},"modified":"2025-03-30T21:34:31","modified_gmt":"2025-03-31T04:34:31","slug":"populating-google-sheets","status":"publish","type":"post","link":"https:\/\/tdengine.com\/populating-google-sheets\/","title":{"rendered":"Populating Google Sheets with Data from TDengine Cloud"},"content":{"rendered":"\n<p>TDengine is a high-performance, open-source and enterprise-ready <a href=\"https:\/\/tdengine.com\/what-is-a-time-series-database\/\">time-series database<\/a>. With TDengine Cloud, you get all of the benefits of the core TDengine platform without having to worry about maintaining infrastructure. TDengine Cloud is very developer-friendly, and you can use client libraries for a variety of languages, including Python, Node.js, and C\/C++, to connect your application to TDengine.<\/p>\n\n\n\n<p>Additionally, TDengine Cloud provides a REST endpoint which gives you tremendous flexibility to connect to TDengine and carry out even complex queries. While enterprises typically use analytics applications like Power BI and Tableau to connect to data sources, and while TDengine supports these applications, SMBs and startups do use more inexpensive solutions like Google Workspace for their enterprise needs. In these cases, Google Apps Script is a fairly powerful tool to perform automation against Google services such as Google Drive and several other Google services.<\/p>\n\n\n\n<p>This article demonstrates how Google Apps Script can be used to fetch data easily from TDengine into Google Sheets, by performing queries against the REST endpoint.<\/p>\n\n\n\n<p>Google Apps Script, for all practical purposes, is JavaScript &#8212; but with Google-provided methods that simplify or extend JavaScript functionality. Google provides a web-based IDE for Google Apps Script projects.<\/p>\n\n\n\n<p>The following procedure creates a Google Apps Script project that will prompt the user for a query and for a database name, perform the query, and then populate a spreadsheet with the data. The headers are populated in the first row of the spreadsheet.<\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li>Create a new Google Sheet document and give it a name.<\/li>\n\n\n\n<li>Click <strong>Extensions<\/strong> &gt; <strong>Apps Script<\/strong>.<\/li>\n<\/ol>\n\n\n\n<figure class=\"gb-block-image gb-block-image-29cd60cf\"><img decoding=\"async\" width=\"480\" height=\"270\" class=\"gb-image gb-image-29cd60cf\" src=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/01-1.png?strip=all&sharp=1\" alt=\"Apps Script is the third item in the Extensions menu.\" srcset=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/01-1.png?strip=all&amp;sharp=1 480w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/01-1-300x169.png?strip=all&amp;sharp=1 300w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/01-1.png?strip=all&amp;sharp=1&amp;w=96 96w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/01-1.png?strip=all&amp;sharp=1&amp;w=192 192w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/01-1.png?strip=all&amp;sharp=1&amp;w=384 384w\" sizes=\"(max-width: 480px) 100vw, 480px\" \/><\/figure>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li>The Google Apps Script IDE is displayed.<\/li>\n<\/ol>\n\n\n\n<figure class=\"gb-block-image gb-block-image-6adaa92b\"><img decoding=\"async\" width=\"1280\" height=\"254\" class=\"gb-image gb-image-6adaa92b\" src=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/02.png?strip=all&sharp=1\" alt=\"The Apps Script IDE is displayed.\" srcset=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/02.png?strip=all&amp;sharp=1 1280w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/02-300x60.png?strip=all&amp;sharp=1 300w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/02-1024x203.png?strip=all&amp;sharp=1 1024w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/02-768x152.png?strip=all&amp;sharp=1 768w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/02.png?strip=all&amp;sharp=1&amp;w=512 512w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/02.png?strip=all&amp;sharp=1&amp;w=450 450w\" sizes=\"(max-width: 1200px) 100vw, 1200px\" \/><\/figure>\n\n\n\n<ol start=\"4\" class=\"wp-block-list\">\n<li>Now you can enter your code. The example code is shown here. Comments in the code provide adequate explanations.<\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code class=\"\" data-line=\"\">function getDataFromTDengine() {\n  \n  \/\/ This function expects the query and db to be supplied\n  \n  query = displayPrompt(&quot;Please enter the query:&quot;);\n  db = displayPrompt(&quot;Please enter the database name you are querying:&quot;)\n  \n  \/\/ This is an example token. Log in to your TDengine Cloud account to find your token.\n  const TDENGINE_CLOUD_TOKEN=&quot;029d9b342xxxxx4c09ef8bf519732d514ae3e69bf47a&quot;;\n  \n  \/\/ This is an example URL. Log in to your TDengine Cloud account to find your cloud URL.\n  const TDENGINE_CLOUD_URL=&quot;https:\/\/gw.us-central-1.gcp.cloud.tdengine.com&quot;;\n\n  \/\/ This is the REST endpoint provided for every TDengine Cloud instance.\n  const restEndPoint = &quot;\/rest\/sql\/&quot;\n\n  \/\/ Make sure the database name is not null or empty.\n  if (db === &quot;&quot; || db === null) {\n    alertMessage(&quot;You must supply a database name.&quot;);\n  }\n  \/\/ Construct the final URL for the request.\n  var finalURL = TDENGINE_CLOUD_URL+ restEndPoint + db + &quot;?token=&quot;+TDENGINE_CLOUD_TOKEN\n  \n  \/\/ Make sure the query is not null or empty.\n  \n  if (query === &quot;&quot; || query === null) {\n    alertMessage(&quot;You must supply a query.&quot;);\n\n  }\n  \n  \/\/ POST request with the query in payload.\n  var options = {\n    &#039;method&#039; : &#039;post&#039;,\n    &#039;contentType&#039; : &#039;application\/x-www-form-urlencoded&#039;,\n    &#039;payload&#039; : query\n  };\n\n  \/\/ Construct the request.\n  const req = UrlFetchApp.getRequest(finalURL,options);\n  \n  \/\/ Make the request.\n  let response = UrlFetchApp.fetch(finalURL,options);\n  var tderesp;\n  \n  \/\/ Make sure the response is OK.\n  if (response.getResponseCode() == 200) { \/\/ If HTTP-status is 200-299\n    \/\/ get the response body.\n    tderesp =  response.getContentText();\n  } \n  else {\n    Logger.log(&quot;HTTP-Error: &quot; + response.getResponseCode());\n    alertMessage(response.getResponseCode());\n  }\n  \n  \/\/ Get the active spreadsheet.\n  const activeSheet = SpreadsheetApp.getActiveSpreadsheet();\n  \n  \/\/ Choose the tab - we use the default tab which is always named &quot;Sheet1&quot;.\n  const sheet = activeSheet.getSheetByName(&quot;Sheet1&quot;);\n\n  \/\/ TDengine returns JSON with the keys - code, column_meta, and data\n  \/\/ if everything went well.\n  \/\/ Otherwise it returns code and desc.\n  const result = JSON.parse(tderesp);\n\n  \/\/ Check for code == 0 (success); otherwise, log error.\n  if (result.code == 0) {\n    \n    \/\/ Get the row headers from the column_meta key.\n    \/\/ Populate row 1.\n    for (let x=0; x &lt; result.column_meta.length; x++) {\n      var label = result.column_meta&#091;x]&#091;0];\n      sheet.getRange(1,x+1).setValue(label);\n    }\n    \/\/ Get the data values and populate them starting at row 2.\n    for (let i=0; i &lt; result.data.length; i++) {\n  \n      const row = result.data&#091;i];\n      for (let j=0; j &lt; result.data&#091;i].length; j++) {\n        sheet.getRange(i+2, j+1).setValue(row&#091;j]);\n      }\n\n    }\n  }\n  else {\n    Logger.log(&quot;TDengine returned code: &quot; + result.code)\n    Logger.log(&quot;Description: &quot; + result.desc)\n  }\n}\n\/\/ This adds a menu item &quot;TDengine&quot; to the menu.\nfunction onOpen(e) {\n  \n  var ui = SpreadsheetApp.getUi();\n  ui.createMenu(&#039;TDengine&#039;)\n      .addItem(&#039;Run Query&#039;, &#039;getDataFromTDengine&#039;)\n      .addToUi();\n}\n\nfunction alertMessage(mesg) {\n  SpreadsheetApp.getUi().alert(mesg);\n}\n\nfunction displayPrompt(mesg) {\n  var ui = SpreadsheetApp.getUi();\n  var result = ui.prompt(mesg);\n  return result.getResponseText();\n}<\/code><\/pre>\n\n\n\n<ol start=\"5\" class=\"wp-block-list\">\n<li>Save the project and click <strong>Deploy<\/strong> in the top right hand corner. Then click <strong>Test Deployments<\/strong>.<\/li>\n<\/ol>\n\n\n\n<figure class=\"gb-block-image gb-block-image-24ed3f50\"><img decoding=\"async\" width=\"1280\" height=\"244\" class=\"gb-image gb-image-24ed3f50\" src=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/03.png?strip=all&sharp=1\" alt=\"Test deployments is the last item in the Deploy menu.\" srcset=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/03.png?strip=all&amp;sharp=1 1280w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/03-300x57.png?strip=all&amp;sharp=1 300w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/03-1024x195.png?strip=all&amp;sharp=1 1024w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/03-768x146.png?strip=all&amp;sharp=1 768w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/03.png?strip=all&amp;sharp=1&amp;w=512 512w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/03.png?strip=all&amp;sharp=1&amp;w=450 450w\" sizes=\"(max-width: 1200px) 100vw, 1200px\" \/><\/figure>\n\n\n\n<ol start=\"6\" class=\"wp-block-list\">\n<li>Choose <strong>Editor Add-on<\/strong> as the deployment type.<\/li>\n<\/ol>\n\n\n\n<figure class=\"gb-block-image gb-block-image-1401f925\"><img decoding=\"async\" width=\"1280\" height=\"631\" class=\"gb-image gb-image-1401f925\" src=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/04.png?strip=all&sharp=1\" alt=\"Editor Add-on is the last option in the Select Type menu.\" srcset=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/04.png?strip=all&amp;sharp=1 1280w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/04-300x148.png?strip=all&amp;sharp=1 300w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/04-1024x505.png?strip=all&amp;sharp=1 1024w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/04-768x379.png?strip=all&amp;sharp=1 768w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/04.png?strip=all&amp;sharp=1&amp;w=512 512w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/04.png?strip=all&amp;sharp=1&amp;w=450 450w\" sizes=\"(max-width: 1200px) 100vw, 1200px\" \/><\/figure>\n\n\n\n<ol start=\"7\" class=\"wp-block-list\">\n<li>Click <strong>Create new test<\/strong> &gt; <strong>Choose document<\/strong> and select the document you created in Step 1.<\/li>\n<\/ol>\n\n\n\n<figure class=\"gb-block-image gb-block-image-fed3ea05\"><img decoding=\"async\" width=\"1280\" height=\"631\" class=\"gb-image gb-image-fed3ea05\" src=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/05.png?strip=all&sharp=1\" alt=\"In the Test document field, specify the document that you created.\" srcset=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/05.png?strip=all&amp;sharp=1 1280w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/05-300x148.png?strip=all&amp;sharp=1 300w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/05-1024x505.png?strip=all&amp;sharp=1 1024w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/05-768x379.png?strip=all&amp;sharp=1 768w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/05.png?strip=all&amp;sharp=1&amp;w=512 512w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/05.png?strip=all&amp;sharp=1&amp;w=450 450w\" sizes=\"(max-width: 1200px) 100vw, 1200px\" \/><\/figure>\n\n\n\n<ol start=\"8\" class=\"wp-block-list\">\n<li>Select the radio button for the test created in Step 7 and click <strong>Execute<\/strong>. This returns you to the spreadsheet. Click on <strong>Extensions<\/strong> and verify that the menu item for TDengine is displayed. Then click <strong>Run Query<\/strong>.<\/li>\n<\/ol>\n\n\n\n<figure class=\"gb-block-image gb-block-image-102c39fc\"><img decoding=\"async\" width=\"892\" height=\"398\" class=\"gb-image gb-image-102c39fc\" src=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/06.png?strip=all&sharp=1\" alt=\"In the Extensions menu, your test script is the last item. It contains a submenu with the Run Query command.\" srcset=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/06.png?strip=all&amp;sharp=1 892w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/06-300x134.png?strip=all&amp;sharp=1 300w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/06-768x343.png?strip=all&amp;sharp=1 768w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/06.png?strip=all&amp;sharp=1&amp;w=178 178w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/06.png?strip=all&amp;sharp=1&amp;w=356 356w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/06.png?strip=all&amp;sharp=1&amp;w=535 535w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/06.png?strip=all&amp;sharp=1&amp;w=713 713w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/06.png?strip=all&amp;sharp=1&amp;w=450 450w\" sizes=\"(max-width: 892px) 100vw, 892px\" \/><\/figure>\n\n\n\n<ol start=\"9\" class=\"wp-block-list\">\n<li>You will be prompted for the query and the database name.<\/li>\n\n\n\n<li>The query should then run and populate the spreadsheet with the results.<\/li>\n<\/ol>\n\n\n\n<p>As you can see, it is fairly straightforward to query TDengine using the REST API to populate a Google Sheet with results. Using the Google Apps Script API for Google Sheets, you can also add charts based on the data that was populated and create simple (or complex) reports.<\/p>\n\n\n\n<p>Once you deploy the script as a real project, the menu item appears in Google Sheets a little differently than when you do a Test Deployment.<\/p>\n\n\n\n<figure class=\"gb-block-image gb-block-image-197e3f50\"><img decoding=\"async\" width=\"892\" height=\"398\" class=\"gb-image gb-image-197e3f50\" src=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/07.png?strip=all&sharp=1\" alt=\"The custom menu from this procedure is shown after the Help menu.\" srcset=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/07.png?strip=all&amp;sharp=1 892w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/07-300x134.png?strip=all&amp;sharp=1 300w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/07-768x343.png?strip=all&amp;sharp=1 768w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/07.png?strip=all&amp;sharp=1&amp;w=178 178w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/07.png?strip=all&amp;sharp=1&amp;w=356 356w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/07.png?strip=all&amp;sharp=1&amp;w=535 535w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/07.png?strip=all&amp;sharp=1&amp;w=713 713w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/07.png?strip=all&amp;sharp=1&amp;w=450 450w\" sizes=\"(max-width: 892px) 100vw, 892px\" \/><\/figure>\n","protected":false},"excerpt":{"rendered":"<p>This article shows how Google Apps Script can be used to fetch data easily from TDengine into Google Sheets.<\/p>\n","protected":false},"author":81,"featured_media":14953,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"content-type":"","footnotes":""},"categories":[21],"tags":[],"ppma_author":[167],"class_list":["post-14947","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-engineering"],"authors":[{"term_id":167,"user_id":81,"is_guest":0,"slug":"chait","display_name":"Chait Diwadkar","avatar_url":{"url":"https:\/\/tdengine.com\/wp-content\/uploads\/29.03-05-cdiwadkar.jpg","url2x":"https:\/\/tdengine.com\/wp-content\/uploads\/29.03-05-cdiwadkar.jpg"},"1":"","2":"","3":"","4":"","5":"","6":"","7":"","8":""}],"_links":{"self":[{"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/posts\/14947","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/users\/81"}],"replies":[{"embeddable":true,"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/comments?post=14947"}],"version-history":[{"count":13,"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/posts\/14947\/revisions"}],"predecessor-version":[{"id":24682,"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/posts\/14947\/revisions\/24682"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/media\/14953"}],"wp:attachment":[{"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/media?parent=14947"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/categories?post=14947"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/tags?post=14947"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/ppma_author?post=14947"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}