%%bash

gcloud config list

"""
Explanation: BigQuery Essentials for Teradata Users
In this lab you will take an existing 2TB+ TPC-DS benchmark dataset and learn common day-to-day activities you'll perform in BigQuery. 
What you'll do
In this lab, you will learn how to:

Use BigQuery to access and query the TPC-DS benchmark dataset
Understand common differences between Teradata and BigQuery
Run pre-defined queries to establish baseline performance benchmarks

BigQuery
BigQuery is Google's fully managed, NoOps, low cost analytics database. With BigQuery you can query terabytes and terabytes of data without managing infrastructure.  BigQuery allows you to focus on analyzing data to find meaningful insights.
TPC-DS Background
In order to benchmark the performance of a data warehouse we first must get tables and data to run queries against. There is a public organization, TPC, that provides large benchmarking datasets to companies explicitly for this purpose. The purpose of TPC benchmarks is to provide relevant, objective performance data to industry users.
The TPC-DS Dataset we will be using comprises of 25 tables and 99 queries that simulate common data analysis tasks. View the full documentation here.
Exploring TPC-DS in BigQuery
The TPC-DS tables have been loaded into BigQuery for you to explore. We have limited the size to 2TB for the timing of this lab but the dataset itself can be expanded as needed.
Note: The TPC Benchmark and TPC-DS are trademarks of the Transaction Processing Performance Council (http://www.tpc.org). The Cloud DW benchmark is derived from the TPC-DS Benchmark and as such is not comparable to published TPC-DS results.
Google Cloud and BigQuery organization
First, a note on resource hierarchy. At the lowest level, resources are the fundamental components that make up all Google Cloud services. Examples of resources include Compute Engine Virtual Machines (VMs), Pub/Sub topics, Cloud Storage buckets, App Engine instances, and BigQuery datasets. All these lower level resources can only be parented by projects, which represent the first grouping mechanism of the Google Cloud resource hierarchy.
You may have noticed you had a project name in the upper left of the console when you opened this notebook:
<img src="img/project.png">
You can also run a local gcloud command to detect what your project and id currently are set:
End of explanation
"""


!bq ls

"""
Explanation: Google Cloud resources are organized hierarchically. Starting from the bottom of the hierarchy, projects are the first level, and they contain other resources. All resources except for organizations have exactly one parent. The Organization is the top of the hierarchy and does not have a parent.
Folders are an additional grouping mechanism on top of projects.
<img src="img/cloud-folders-hierarchy.png">
For the purposes of a BigQuery user, this is helpful to know as access management policies (IAM) and Organizational policies are largely imposed at the project, folder, or organizational level. Also, BigQuery "Reservations", or chunks of allocated Bigcompute (but not storage) are currently assigned at the project or folder level.
BigQuery Datasets
<p>A dataset is contained within a specific <a href="https://cloud.google.com/bigquery/docs/projects">project</a>. Datasets
  are top-level containers that are used to organize and control access to your
  <a href="https://cloud.google.com/bigquery/docs/tables">tables</a> and <a href="https://cloud.google.com/bigquery/docs/views">views</a>. A table
  or view must belong to a dataset, so you need to create at least one dataset before
  <a href="https://cloud.google.com/bigquery/docs/loading-data">loading data into BigQuery</a>.</p>

BigQuery datasets are subject to the following limitations:

You can set the geographic location at creation time only. After a dataset has
  been created, the location becomes immutable and can't be changed by using the
  Console, using the bq tool, or calling the patch or
  update API methods.
All tables that are referenced in a query must be stored in datasets in the
  same location
When you copy a table, the
  datasets that contain the source table and destination table must reside in
  the same location.
Dataset names must be unique for each project.

How many datasets are in your current project? Run the following to find out:
End of explanation
"""


!bq ls --project_id qwiklabs-resources

"""
Explanation: For this lab, you will be accessing data stored in another project, in this case a publically accessible sample project qwiklabs-resources. See how many datasets exist in this project:
End of explanation
"""


!bq ls --project_id qwiklabs-resources tpcds_2t_baseline

"""
Explanation: And let's look at the tables and views in one of these datasets:
End of explanation
"""


%%bigquery
SELECT 
  dataset_id,
  table_id,
  -- Convert bytes to GB.
  ROUND(size_bytes/pow(10,9),2) as size_gb,
  -- Convert UNIX EPOCH to a timestamp.
  TIMESTAMP_MILLIS(creation_time) AS creation_time,
  TIMESTAMP_MILLIS(last_modified_time) as last_modified_time,
  row_count,
  CASE 
    WHEN type = 1 THEN 'table'
    WHEN type = 2 THEN 'view'
  ELSE NULL
  END AS type
FROM
  `qwiklabs-resources.tpcds_2t_baseline.__TABLES__`
ORDER BY size_gb DESC

"""
Explanation: But how are we able to access other data? And won't querying that data create work in that user's cluster? Not at all! Because BigQuery has completely separated the compute and storage layers so they can scale independently, we can easily query data (so long as we have permissions) that are in public datasets or datasets from other teams, without incurring compute costs for them, and without slowing their queries down, even if we're accessing the same data.
To explain why, we dive a little deeper into the architecture of BigQuery.
BigQuery Architecture
BigQuery’s serverless architecture decouples storage and compute and allows them to scale independently on demand. This structure offers both immense flexibility and cost controls for customers because they don’t need to keep their expensive compute resources up and running all the time. This is very different from traditional node-based cloud data warehouse solutions or on-premise massively parallel processing (MPP) systems. This approach also allows customers of any size to bring their data into the data warehouse and start analyzing their data using Standard SQL without worrying about database operations and system engineering.
<img src="img/bq_explained_2.jpg">
Under the hood, BigQuery employs a vast set of multi-tenant services driven by low-level Google infrastructure technologies like Dremel, Colossus, Jupiter and Borg.
<img src="img/bq_explained_3.jpg">
Compute is Dremel, a large multi-tenant cluster that executes SQL queries.
Dremel turns SQL queries into distributed, scaled-out execution plans. The nodes of these execution plans are called slots and do the heavy lifting of reading data from storage and any necessary computation. 
Dremel dynamically apportions slots to queries on an as-needed basis, maintaining fairness for concurrent queries from multiple users. A single user can get thousands of slots to run their queries. These slots are assigned just-in-time to your query, and the moment that unit of work is done it gets assigned new work, potentially for someone else's query. This is how BigQuery is able to execute so quickly at low cost. You don't have to over-provision resources like you would with statically sized clusters.
Storage is Colossus, Google’s global storage system.
BigQuery leverages the columnar storage format and compression algorithm to store data in Colossus, optimized for reading large amounts of structured data. This is the same technology powering Google Cloud's blog storage services - GCS.
Colossus also handles replication, recovery (when disks crash) and distributed management (so there is no single point of failure). Colossus allows BigQuery users to scale to dozens of petabytes of data stored seamlessly, without paying the penalty of attaching much more expensive compute resources as in traditional data warehouses.
Compute and storage talk to each other through the petabit Jupiter network.
In between storage and compute is ‘shuffle’, which takes advantage of Google’s Jupiter network to move data extremely rapidly from one place to another.
BigQuery is orchestrated via Borg, Google’s precursor to Kubernetes.
The mixers and slots are all run by Borg, which allocates hardware resources. Essentially, a single BigQuery 'cluster' is able to run thousands of physical machines at once and be securely shared between users, giving massive compute power just-in-time to those who need it.
What does this mean for you?
Working with BigQuery is different. Some concepts that are important:
* Compute and storage are separate and storage is CHEAP - making copies of data will not waste compute space on nodes like in previous systems. It is also easy to set a TTL on temporary datasets and tables to the garbage collect automatically.
* The 'workers' in bigquery are called Slots. These are scheduled fairly amongst all the users and queries within a project. Sometimes your query is bound by the amount of parallelism that BigQuery can achieve. Sometimes it is bound by the number of slots available to your organization - hence getting more slots will speed it up
* While your organization may have a reservation for Slots, meaning a guaranteed number of compute power available to teams, your organization doesn't have it's own BigQuery cluster, per se. It is running in a much larger installation of BigQuery, shared securely amongst other customers. This means you can easily increase and decrease the amount of slots your organization has reserved at a moment's notice with Flex Slots.
Exploring the TPC-DS Schema with SQL
Question: 
- How many tables are in the dataset?
- What is the name of the largest table (in GB)? How many rows does it have?
- Note the FROM clause - which identifier is the project, which is the datasets, and which is the table or view?
End of explanation
"""


%%bigquery
SELECT * FROM 
 `qwiklabs-resources.tpcds_2t_baseline.INFORMATION_SCHEMA.COLUMNS`

"""
Explanation: The core tables in the data warehouse are derived from 5 separate core operational systems (each with many tables):

These systems are driven by the core functions of our retail business. As you can see, our store accepts sales from online (web), mail-order (catalog), and in-store. The business must keep track of inventory and can offer promotional discounts on items sold. 
Exploring all available columns of data
Question:
- How many columns of data are in the entire dataset (all tables)?
End of explanation
"""


%%bigquery
SELECT * FROM 
 `qwiklabs-resources.tpcds_2t_baseline.INFORMATION_SCHEMA.COLUMNS`
WHERE 
  is_partitioning_column = 'YES' OR clustering_ordinal_position IS NOT NULL

"""
Explanation: Question:
- Are any of the columns of data in this baseline dataset partitioned or clustered? (This will be covered in another lab)
End of explanation
"""


%%bigquery
SELECT 
  COUNT(column_name) AS column_count, 
  table_name 
FROM 
 `qwiklabs-resources.tpcds_2t_baseline.INFORMATION_SCHEMA.COLUMNS`
GROUP BY table_name
ORDER BY column_count DESC, table_name

"""
Explanation: Question
- How many columns of data does each table have (sorted by most to least?)
- Which table has the most columns of data?
End of explanation
"""


!bq show qwiklabs-resources:tpcds_2t_baseline.catalog_sales

"""
Explanation: Previewing sample rows of data values
Click on the catalog_sales table name for the tpcds_2t_baseline dataset under qwiklabs-resources
Question
- How many rows are in the table?
- How large is the table in TB?
End of explanation
"""


!bq head -n 15 --selected_fields "cs_order_number,cs_quantity,cs_ext_sales_price,cs_ext_ship_cost"  qwiklabs-resources:tpcds_2t_baseline.catalog_sales 

"""
Explanation: Question:
- Preview the data and find the Catalog Sales Extended Sales Price cs_ext_sales_price field (which is calculated based on product quantity * sales price)
- Are there any missing data values for Catalog Sales Quantity (cs_quantity)? 
- Are there any missing values for cs_ext_ship_cost? For what type of product could this be expected? (Digital products)
We are using the bq head command line tool to avoid a full table scan with a SELECT * LIMIT 15
End of explanation
"""


%%bigquery --verbose
--Query should fail

SELECT
  
FROM
  `qwiklabs-resources.tpcds_2t_baseline.catalog_sales`

LIMIT
  10

%%bigquery --verbose
--Query should succeed

SELECT
  cs_item_sk,
  COUNT(cs_order_number) AS total_orders,
  SUM(cs_quantity) AS total_quantity,
  SUM(cs_ext_sales_price) AS total_revenue,
  SUM(cs_net_profit) AS total_profit
FROM
  `qwiklabs-resources.tpcds_2t_baseline.catalog_sales`
GROUP BY
  cs_item_sk
ORDER BY
  total_orders DESC
LIMIT
  10

"""
Explanation: Create an example sales report
TODO(you): Write a query that shows key sales stats for each item sold from the Catalog and execute it here:
- total orders
- total unit quantity
- total revenue
- total profit
- sorted by total orders highest to lowest, limit 10
End of explanation
"""


%%bigquery

SELECT
  project_id,
  job_id,
  query,
  cache_hit,
  reservation_id,
  EXTRACT(DATE FROM creation_time) AS creation_date,
  creation_time,
  end_time,
  TIMESTAMP_DIFF(end_time, start_time, SECOND) AS job_duration_seconds,
  job_type,
  user_email,
  state,
  error_result,
  total_bytes_processed,
  total_slot_ms / 1000 / 60 AS slot_minutes,
  -- Average slot utilization per job is calculated by dividing
  -- total_slot_ms by the millisecond duration of the job
  total_slot_ms / (TIMESTAMP_DIFF(end_time, start_time, MILLISECOND)) AS avg_slots
FROM
  `region-us`.INFORMATION_SCHEMA.JOBS_BY_PROJECT
ORDER BY
  creation_time DESC
LIMIT 15;

!bq ls -j -a -n 15

"""
Explanation: A note on our data: The TPC-DS benchmark allows data warehouse practitioners to generate any volume of data programmatically. Since the rows of data are system generated, they may not make the most sense in a business context (like why are we selling our top product at such a huge profit loss!).
The good news is that to benchmark our performance we care most about the volume of rows and columns to run our benchmark against. 
Analyzing query performance
You can use the INFORMATION_SCHEMA to inspect your query performance. A lot of this data is also presented in the UI under Execution Details.
Refer to the query below (which should be similar to your results) and answer the following questions.
Question
- How long did it take the query to run? 14s
- How much data in GB was processed? 150GB
- How much slot time was consumed? 1hr 7min
End of explanation
"""


!head --lines=50 'sql/example_baseline_queries.sql'

"""
Explanation: Side note: Slot Time
We know the query took 15 seconds to run so what does the 1hr 7 min slot time metric mean?
Inside of the BigQuery service are lots of virtual machines that massively process your data and query logic in parallel. These workers, or "slots", work together to process a single query job really quickly. For accounts with on-demand pricing, you can have up to 2,000 slots.
So say we had 30 minutes of slot time or 1800 seconds. If the query took 20 seconds in total to run, 
but it was 1800 seconds worth of work, how many workers at minimum worked on it? 
1800/20 = 90
And that's assuming each worker instantly had all the data it needed (no shuffling of data between workers) and was at full capacity for all 20 seconds!
In reality, workers have a variety of tasks (waiting for data, reading it, performing computations, and writing data)
and also need to compare notes with each other on what work was already done on the job. The good news for you is
that you don't need to worry about optimizing these workers or the underlying data to run perfectly in parallel. That's why BigQuery is a managed service -- there's an entire team dedicated to hardware and data storage optimization.
The "avg_slots" metric indicates the average number of slots being utilized by your query at any given time. Often, portions of the query plan will have different amounts of parallelism and thus can benefit (or not) from more slots. For example, if you're performing a basic READ+FILTER+AGGREGATE query, reading data from a large table may require 1,000 slots for the INPUT phase since each slot reads a file, but if a lot of the data is immediately filtered, there may be fewer slots or even one slot needed for the next stage to aggregate. Certain portions of your queries may become bottlenecks for parallelism, for example, JOINs, SORTs, etc. BigQuery can execute many of these in a parallel manner and optimizing this queries is a more advanced topic. At this point, it's important to know slot_time, and conceptually what a slot is.
In case you were wondering, the worker limit for your project is 2,000 slots at once. In a production setting, this will vary depending on whether your organization is using "flat-rate" pricing on "on-demand". If you're "flat-rate", the amount of slots will depend on the organization's reservation, how that reservations is apportioned to different folders, projects, and teams, and how busy each slice of the reservation is at any given moment.
Running a performance benchmark
To performance benchmark our data warehouse in BigQuery we need to create more than just a single SQL report. The good news is the TPC-DS dataset ships with 99 standard benchmark queries that we can run and log the performance outcomes. 
In this lab, we are doing no adjustments to the existing data warehouse tables (no partitioning, no clustering, no nesting) so we can establish a performance benchmark to beat in future labs.
Viewing the 99 pre-made SQL queries
We have a long SQL file with 99 standard queries against this dataset stored in our /sql/ directory.
Let's view the first 50 lines of those baseline queries to get familiar with how we will be performance benchmarking our dataset.
End of explanation
"""


%%bigquery --verbose
# start query 1 in stream 0 using template query96.tpl
select  count(*) 
from `qwiklabs-resources.tpcds_2t_baseline.store_sales` as store_sales
    ,`qwiklabs-resources.tpcds_2t_baseline.household_demographics` as household_demographics 
    ,`qwiklabs-resources.tpcds_2t_baseline.time_dim` as time_dim, 
    `qwiklabs-resources.tpcds_2t_baseline.store` as store
where ss_sold_time_sk = time_dim.t_time_sk   
    and ss_hdemo_sk = household_demographics.hd_demo_sk 
    and ss_store_sk = s_store_sk
    and time_dim.t_hour = 8
    and time_dim.t_minute >= 30
    and household_demographics.hd_dep_count = 5
    and store.s_store_name = 'ese'
order by count(*)
limit 100;

"""
Explanation: Running the first benchmark test
Now let's run the first query against our dataset and note the execution time. Tip: You can use the --verbose flag in %%bigquery magics to return the job and completion time.
End of explanation
"""


!bq ls -j -a -n 5

"""
Explanation: It should execute in just a few seconds. Then try running it again and see if you get the same performance. BigQuery will automatically cache the results from the first time you ran the query and then serve those same results to you when you can the query again. We can confirm this by analyzing the query job statistics. 
Viewing BigQuery job statistics
Let's list our five most recent query jobs run on BigQuery using the bq command line interface. Then we will get even more detail on our most recent job with the bq show command.
End of explanation
"""


!bq show --format=prettyjson -j fae46669-5e96-4744-9d2c-2b1b95fa21e7

"""
Explanation: Be sure to replace the job id with your own most recent job.
End of explanation
"""


%%bash 
bq query \
--dry_run \
--nouse_cache \
--use_legacy_sql=false \
"""\
select  count(*) 
from \`qwiklabs-resources.tpcds_2t_baseline.store_sales\` as store_sales
    ,\`qwiklabs-resources.tpcds_2t_baseline.household_demographics\` as household_demographics  
    ,\`qwiklabs-resources.tpcds_2t_baseline.time_dim\` as time_dim, \`qwiklabs-resources.tpcds_2t_baseline.store\` as store
where ss_sold_time_sk = time_dim.t_time_sk   
    and ss_hdemo_sk = household_demographics.hd_demo_sk 
    and ss_store_sk = s_store_sk
    and time_dim.t_hour = 8
    and time_dim.t_minute >= 30
    and household_demographics.hd_dep_count = 5
    and store.s_store_name = 'ese'
order by count(*)
limit 100;
"""

# Convert bytes to GB
132086388641 / 1e+9

"""
Explanation: Looking at the job statistics we can see our most recent query hit cache 
- cacheHit: true and therefore 
- totalBytesProcessed: 0. 
While this is great in normal uses for BigQuery (you aren't charged for queries that hit cache) it kind of ruins our performance test. While cache is super useful we want to disable it for testing purposes.
Disabling Cache and Dry Running Queries
As of the time this lab was created, you can't pass a flag to %%bigquery iPython notebook magics to disable cache or to quickly see the amount of data processed. So we will use the traditional bq command line interface in bash.
First we will do a dry run of the query without processing any data just to see how many bytes of data would be processed. Then we will remove that flag and ensure nouse_cache is set to avoid hitting cache as well.
End of explanation
"""


%%bash 
bq query \
--nouse_cache \
--use_legacy_sql=false \
"""\
select  count(*) 
from \`qwiklabs-resources.tpcds_2t_baseline.store_sales\` as store_sales
    ,\`qwiklabs-resources.tpcds_2t_baseline.household_demographics\` as household_demographics  
    ,\`qwiklabs-resources.tpcds_2t_baseline.time_dim\` as time_dim, \`qwiklabs-resources.tpcds_2t_baseline.store\` as store
where ss_sold_time_sk = time_dim.t_time_sk   
    and ss_hdemo_sk = household_demographics.hd_demo_sk 
    and ss_store_sk = s_store_sk
    and time_dim.t_hour = 8
    and time_dim.t_minute >= 30
    and household_demographics.hd_dep_count = 5
    and store.s_store_name = 'ese'
order by count(*)
limit 100;
"""

"""
Explanation: 132 GB will be processed. At the time of writing, BigQuery pricing is \$5 per 1 TB (or 1000 GB) of data after the first free 1 TB each month. Assuming we've exhausted our 1 TB free this month, this would be \$0.66 to run.
Now let's run it an ensure we're not pulling from cache so we get an accurate time-to-completion benchmark.
End of explanation
"""


%%bash

export PROJECT_ID=$(gcloud config list --format 'value(core.project)')
export BENCHMARK_DATASET_NAME=tpcds_2t_baseline # Name of the dataset you want to create

## Create a BigQuery dataset for tpcds_2t_flat_part_clust if it doesn't exist
datasetexists=$(bq ls -d | grep -w $BENCHMARK_DATASET_NAME)

if [ -n "$datasetexists" ]; then
    echo -e "BigQuery dataset $BENCHMARK_DATASET_NAME already exists, let's not recreate it."

else
    echo "Creating BigQuery dataset titled: $BENCHMARK_DATASET_NAME"
    
    bq --location=US mk --dataset \
        --description 'Benchmark Dataset' \
        $PROJECT:$BENCHMARK_DATASET_NAME

fi

# Inspect your project and datasets
!bq ls 
!bq ls tpcds_2t_baseline

"""
Explanation: If you're an experienced BigQuery user, you likely have seen these same metrics in the Web UI as well as highlighted in the red box below:

It's a matter of preference whether you do your work in the Web UI or the command line -- each has it's advantages.
One major advantage of using the bq command line interface is the ability to create a script that will run the remaining 98 benchmark queries for us and log the results. 
Copy the qwiklabs-resources dataset into your own GCP project
We will use the new BigQuery Transfer Service to quickly copy our large dataset from the qwiklabs-resources GCP project into your own so you can perform the benchmarking. 
Create a new baseline dataset in your project
End of explanation
"""


%%bash

# Should take about 30 seconds, starts a bunch of asynchronous copy jobs


bq cp -nosync qwiklabs-resources:tpcds_2t_baseline.call_center tpcds_2t_baseline.call_center
bq cp -nosync qwiklabs-resources:tpcds_2t_baseline.catalog_page tpcds_2t_baseline.catalog_page
bq cp -nosync qwiklabs-resources:tpcds_2t_baseline.catalog_returns tpcds_2t_baseline.catalog_returns
bq cp -nosync qwiklabs-resources:tpcds_2t_baseline.catalog_sales tpcds_2t_baseline.catalog_sales
bq cp -nosync qwiklabs-resources:tpcds_2t_baseline.customer tpcds_2t_baseline.customer
bq cp -nosync qwiklabs-resources:tpcds_2t_baseline.customer_address tpcds_2t_baseline.customer_address
bq cp -nosync qwiklabs-resources:tpcds_2t_baseline.customer_demographics tpcds_2t_baseline.customer_demographics
bq cp -nosync qwiklabs-resources:tpcds_2t_baseline.date_dim tpcds_2t_baseline.date_dim
bq cp -nosync qwiklabs-resources:tpcds_2t_baseline.dbgen_version tpcds_2t_baseline.dbgen_version
bq cp -nosync qwiklabs-resources:tpcds_2t_baseline.household_demographics tpcds_2t_baseline.household_demographics
bq cp -nosync qwiklabs-resources:tpcds_2t_baseline.income_band tpcds_2t_baseline.income_band
bq cp -nosync qwiklabs-resources:tpcds_2t_baseline.inventory tpcds_2t_baseline.inventory
bq cp -nosync qwiklabs-resources:tpcds_2t_baseline.item tpcds_2t_baseline.item
bq cp -nosync qwiklabs-resources:tpcds_2t_baseline.perf tpcds_2t_baseline.perf
bq cp -nosync qwiklabs-resources:tpcds_2t_baseline.promotion tpcds_2t_baseline.promotion
bq cp -nosync qwiklabs-resources:tpcds_2t_baseline.reason tpcds_2t_baseline.reason
bq cp -nosync qwiklabs-resources:tpcds_2t_baseline.ship_mode tpcds_2t_baseline.ship_mode
bq cp -nosync qwiklabs-resources:tpcds_2t_baseline.store tpcds_2t_baseline.store
bq cp -nosync qwiklabs-resources:tpcds_2t_baseline.store_returns tpcds_2t_baseline.store_returns
bq cp -nosync qwiklabs-resources:tpcds_2t_baseline.store_sales tpcds_2t_baseline.store_sales
bq cp -nosync qwiklabs-resources:tpcds_2t_baseline.time_dim tpcds_2t_baseline.time_dim
bq cp -nosync qwiklabs-resources:tpcds_2t_baseline.warehouse tpcds_2t_baseline.warehouse
bq cp -nosync qwiklabs-resources:tpcds_2t_baseline.web_page tpcds_2t_baseline.web_page
bq cp -nosync qwiklabs-resources:tpcds_2t_baseline.web_returns tpcds_2t_baseline.web_returns
bq cp -nosync qwiklabs-resources:tpcds_2t_baseline.web_sales tpcds_2t_baseline.web_sales
bq cp -nosync qwiklabs-resources:tpcds_2t_baseline.web_site tpcds_2t_baseline.web_site
    

"""
Explanation: Here we will use the bq cp command to copy tables over. If you need to periodically refresh data, the BQ Transfer service or scheduled queries are good tools as well.
End of explanation
"""


!bq ls tpcds_2t_baseline

"""
Explanation: Inspect the tables now in your project.
End of explanation
"""


%%bigquery
SELECT COUNT(*) AS store_transaction_count
FROM tpcds_2t_baseline.store_sales

"""
Explanation: Verify you now have the baseline data in your project
Run the below query and confirm you see data. Note that if you omit the project-id ahead of the dataset name in the FROM clause, BigQuery will assume your default project.
End of explanation
"""


%%bash
# runs the SQL queries from the TPCDS benchmark 

# Pull the current Google Cloud Platform project name

BQ_DATASET="tpcds_2t_baseline" # let's start by benchmarking our baseline dataset 
QUERY_FILE_PATH="./sql/example_baseline_queries.sql" # the full test is on 99_baseline_queries but that will take 80+ mins to run
IFS=";"

# create perf table to keep track of run times for all 99 queries
printf "\033[32;1m Housekeeping tasks... \033[0m\n\n";
printf "Creating a reporting table perf to track how fast each query runs...";
perf_table_ddl="CREATE TABLE IF NOT EXISTS $BQ_DATASET.perf(performance_test_num int64, query_num int64, elapsed_time_sec int64, ran_on int64)"
bq rm -f $BQ_DATASET.perf
bq query --nouse_legacy_sql $perf_table_ddl 

start=$(date +%s)
index=0
for select_stmt in $(<$QUERY_FILE_PATH)　
do 
  # run the test until you hit a line with the string 'END OF BENCHMARK' in the file
  if [[ "$select_stmt" == *'END OF BENCHMARK'* ]]; then
    break
  fi

  printf "\n\033[32;1m Let's benchmark this query... \033[0m\n";
  printf "$select_stmt";
  
  SECONDS=0;
  bq query --use_cache=false --nouse_legacy_sql $select_stmt # critical to turn cache off for this test
  duration=$SECONDS

  # get current timestamp in milliseconds  
  ran_on=$(date +%s)

  index=$((index+1))

  printf "\n\033[32;1m Here's how long it took... \033[0m\n\n";
  echo "Query $index ran in $(($duration / 60)) minutes and $(($duration % 60)) seconds."

  printf "\n\033[32;1m Writing to our benchmark table... \033[0m\n\n";
  insert_stmt="insert into $BQ_DATASET.perf(performance_test_num, query_num, elapsed_time_sec, ran_on) values($start, $index, $duration, $ran_on)"
  printf "$insert_stmt"
  bq query --nouse_legacy_sql $insert_stmt
done

end=$(date +%s)

printf "Benchmark test complete"


"""
Explanation: Setup an automated test
Running each of the 99 queries manually via the Console UI would be a tedious effort. We'll show you how you can run all 99 programmatically and automatically log the output (time and GB processed) to a log file for analysis. 
Below is a shell script that:
1. Accepts a BigQuery dataset to benchmark
2. Accepts a list of semi-colon separated queries to run
3. Loops through each query and calls the bq query command
4. Records the execution time into a separate BigQuery performance table perf
Execute the below statement and follow along with the results as you benchmark a few example queries (don't worry, we've already ran the full 99 recently so you won't have to).
After executing, wait 1-2 minutes for the benchmark test to complete
End of explanation
"""


%%bigquery
SELECT * FROM tpcds_2t_baseline.perf
WHERE 
 # Let's only pull the results from our most recent test
 performance_test_num = (SELECT MAX(performance_test_num) FROM tpcds_2t_baseline.perf)
ORDER BY ran_on

"""
Explanation: Viewing the benchmark results
As part of the benchmark test, we stored the processing time of each query into a new perf BigQuery table. We can query that table and get some performance stats for our test. 
First are each of the tests we ran:
End of explanation
"""


%%bigquery
SELECT
  TIMESTAMP_SECONDS(MAX(performance_test_num)) AS test_date,
  MAX(performance_test_num) AS latest_performance_test_num,
  COUNT(DISTINCT query_num) AS count_queries_benchmarked,
  SUM(elapsed_time_sec) AS total_time_sec,
  MIN(elapsed_time_sec) AS fastest_query_time_sec,
  MAX(elapsed_time_sec) AS slowest_query_time_sec
FROM
  tpcds_2t_baseline.perf
WHERE
  performance_test_num = (SELECT MAX(performance_test_num) FROM tpcds_2t_baseline.perf)

"""
Explanation: And finally, the overall statistics for the entire test:
End of explanation
"""


