<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Ayokunle Adeniyi</title>
    <description>The latest articles on Forem by Ayokunle Adeniyi (@ayokunle).</description>
    <link>https://forem.com/ayokunle</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1277820%2F6791ec7d-944f-4d66-ba85-5647077dd609.png</url>
      <title>Forem: Ayokunle Adeniyi</title>
      <link>https://forem.com/ayokunle</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ayokunle"/>
    <language>en</language>
    <item>
      <title>Learn about the history of the data stack here</title>
      <dc:creator>Ayokunle Adeniyi</dc:creator>
      <pubDate>Thu, 05 Mar 2026 10:46:24 +0000</pubDate>
      <link>https://forem.com/ayokunle/-1lcc</link>
      <guid>https://forem.com/ayokunle/-1lcc</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/aws-builders" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F2794%2F88da75b6-aadd-4ea1-8083-ae2dfca8be94.png" alt="AWS Community Builders " width="350" height="350"&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1277820%2F6791ec7d-944f-4d66-ba85-5647077dd609.png" alt="" width="420" height="420"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/aws-builders/the-evolution-of-the-modern-data-stack-from-rdbms-to-the-lakehouse-588a" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;The evolution of the Modern Data Stack: From RDBMS to the LakeHouse&lt;/h2&gt;
      &lt;h3&gt;Ayokunle Adeniyi for AWS Community Builders  ・ Jan 13&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#architecture&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#dataengineering&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#learning&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#discuss&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>architecture</category>
      <category>dataengineering</category>
      <category>learning</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Learnings from Pursuing High Data Quality: A Reflective Piece</title>
      <dc:creator>Ayokunle Adeniyi</dc:creator>
      <pubDate>Thu, 05 Mar 2026 10:00:00 +0000</pubDate>
      <link>https://forem.com/aws-builders/learnings-from-pursuing-high-data-quality-a-reflective-piece-57af</link>
      <guid>https://forem.com/aws-builders/learnings-from-pursuing-high-data-quality-a-reflective-piece-57af</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Cover Photo by &lt;a href="https://unsplash.com/@purzlbaum?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Claudio Schwarz&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/a-close-up-of-a-window-with-a-building-in-the-background-fyeOxvYvIyY?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Before diving in, let us discuss what data quality entails. IBM puts it so well - Data quality measures how well a dataset meets criteria for accuracy, completeness, validity, consistency, uniqueness, timeliness and fitness for purpose, and it is critical to all data governance initiatives within an organisation. Data quality does not only care about the data being pristine, but also being fit for intended use, meaning that data quality is context-specific. The domain in which the data is collected and used is as important as the other checks. In fact, in many situations, it provides the foundation to define checks for accuracy, validity, consistency, timeliness and uniqueness. Furthermore, data quality can build or destroy trust within the team.&lt;/p&gt;

&lt;p&gt;This is a reflective piece that encapsulates my experience setting up a roadmap for maintaining high-quality data. When asked previously about data quality enforcement and implementation, I always responded with “we can use the tool, or that tech to achieve this”. In practice, I was faced with a rude awakening about how limited that response was.&lt;/p&gt;

&lt;p&gt;To provide a better context, let us define a particular metric where a group of skilled data analysts and scientists were asked to calculate the same metric for a product for a given time window - month, weeks, days, etc. The result was that everyone came with a different number representing that metric. These inconsistencies erode trust, and the output of any data process is predicated on trustworthiness.  It matters because if people get different numbers for a metric, then the question is, “Is the data being collected good? Are we introducing errors during processing?"&lt;/p&gt;

&lt;p&gt;This was the point at which I realised that data quality is not just about the tools. It is about key elements that must be present for the effective delivery of data products. I categorise them into 3 main elements. These elements are process, people, and technology, all working together and in harmony. I will expand on process, people, and technology in the following sections of this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  People
&lt;/h2&gt;

&lt;p&gt;As long as the data is going to be read and interpreted by more than one person, the people element must be considered. While this may seem like a downstream activity, it is essential to be addressed as early as possible in any workflow, request, whether automated, routine, or ad hoc.&lt;br&gt;
To make this practical, the quality of the data starts from understanding the request being made. There must be a free flow of knowledge among all stakeholders in the delivery of any analysis. This means, when a senior executive asks, “What is the day 3 retention for product A?”, instead of going ahead to write fancy SQL and Python scripts, it is worth responding with clarifying questions such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do you mean classic retention or rolling retention?&lt;/li&gt;
&lt;li&gt;For a global product, do you want regional retention that may show regional patterns?&lt;/li&gt;
&lt;li&gt;Should the retention be in UTC 24-hour cycles, and so on?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What this does is either give you clarity on what exactly needs to be calculated or give leeway for assumptions to be made. Overall, the quality of the resulting data and its interpretation depends on people communicating effectively. More so, in a team where analysts and engineers work collaboratively, clear definitions must be documented and made available to produce good downstream data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technology
&lt;/h2&gt;

&lt;p&gt;The tooling list is ever-increasing these days. With tools like &lt;a href="https://greatexpectations.io/" rel="noopener noreferrer"&gt;Great Expectations&lt;/a&gt;, &lt;a href="https://aws.amazon.com/blogs/big-data/test-data-quality-at-scale-with-deequ/" rel="noopener noreferrer"&gt;Amazon Deequ&lt;/a&gt;, &lt;a href="https://soda.io/" rel="noopener noreferrer"&gt;SODA&lt;/a&gt;, and platform-embedded tools like the validation rules in &lt;a href="https://www.getdbt.com/product/build-trust-in-data-and-data-teams" rel="noopener noreferrer"&gt;DBT&lt;/a&gt;, &lt;a href="https://aws.amazon.com/glue/features/data-quality/" rel="noopener noreferrer"&gt;AWS Glue Data Quality&lt;/a&gt; and other similar tools, data quality checks are a solved problem technologically. The only questions worth asking are on cost, competencies of the team, and best fit with the existing tech stack; basically, the checks that occur during a tool assessment. These tools do a good job of creating valid definitions of what the data should have. Typical examples of what these tools provide are consistent ways to create, store and report the results of these checks.&lt;br&gt;
Additionally, it is now a commonplace practice to treat data processing and analysis efforts similarly to software development practices. Therefore, writing maintainable, readable, and modifiable modular code becomes a requirement to foster collaboration and longevity, rather than luxury. Using version control systems like Git becomes a non-negotiable in achieving this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Process
&lt;/h2&gt;

&lt;p&gt;We have seen how people working together in harmony play a vital role in aligning on expectations. I consider ‘process’ to be the wrapper around people and technology. Good processes foster seamless interaction among people and with tools to achieve defined goals. For instance, a group of data engineers and data analysts define a process using the Write-Audit-Publish (WAP) pattern with data quality and validation tests at the audit layer. Therefore, no data product is published without passing all defined tests. Alternatively, large datasets might have preliminary checks to leverage fail-fast mechanisms.&lt;br&gt;
Building effective processes is not always straightforward. Too many steps in a process make achieving a goal too tedious. Alternatively, processes that are too simple and lenient may lack the robustness to define the safe boundaries and guidelines required for consistent and sustainable results. A good process strikes that balance, and it may take several iterations to build that process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;It is tempting to argue that data quality can be achieved with only tooling, but without the right processes, tools become useless, and without the right people and a commitment to uphold a structure, processes are easily circumvented. This is what really matters. The absence of technology, process and people all working in harmony creates a fragile data quality framework for any organisation or project. Furthermore, the ideas discussed may not be seen as data quality but instead as an aspect of data governance. Also, there is a strong alignment with these same principles in the concept behind data contracts. Overall, the intricate application of data governance, data contracts, data quality frameworks or whatever it may be called in an organisation will rely on these and more.&lt;/p&gt;

</description>
      <category>data</category>
      <category>learning</category>
      <category>discuss</category>
      <category>datascience</category>
    </item>
    <item>
      <title>The evolution of the Modern Data Stack: From RDBMS to the LakeHouse</title>
      <dc:creator>Ayokunle Adeniyi</dc:creator>
      <pubDate>Tue, 13 Jan 2026 11:30:00 +0000</pubDate>
      <link>https://forem.com/aws-builders/the-evolution-of-the-modern-data-stack-from-rdbms-to-the-lakehouse-588a</link>
      <guid>https://forem.com/aws-builders/the-evolution-of-the-modern-data-stack-from-rdbms-to-the-lakehouse-588a</guid>
      <description>&lt;p&gt;This post aims to provide a historical picture of the evolution of the typical data stack over a span of about 5 decades. A lot has happened, but I will try to keep it as simple and digestible as possible.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1mnzj33if0itk7biif9y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1mnzj33if0itk7biif9y.png" alt="Evolution of the tools, concepts and technologies in the modern data stack" width="800" height="258"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Fig 1: Evolution of the tools, concepts and technologies in the modern data stack&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1970 - 1980s: The relational model, SQL, ACID and the RDBMS.
&lt;/h3&gt;

&lt;p&gt;It all started with the relational model invented by Edgar F. Codd in the 1970s in his &lt;a href="https://www.seas.upenn.edu/~zives/03f/cis550/codd.pdf" rel="noopener noreferrer"&gt;paper&lt;/a&gt;. Codd proposes the following: That data can be structured as a set of tuples (rows) where each value belongs to a field/attribute (column), altogether making a table that consists of a primary key that relates to other tables, by being a foreign key on other tables. He also proposes that users should not care about how the data is stored, that changes to the data should not break the applications using them. Lastly, he introduces normalisation, and that a language exists where users can consistently access and modify data. Within the next decade, specifically 1983, by Theo Härder and Andreas Reuter, the popular &lt;a href="https://cs-people.bu.edu/mathan/reading-groups/papers-classics/recovery.pdf" rel="noopener noreferrer"&gt;ACID&lt;/a&gt; (atomicity, consistency, isolation and durability) principles were established. These principles are important to mention as they are still relevant today and are at the centre of many of the innovations happening in the data stack infrastructure space. The relational model and ACID principles found very high adoption and utility in OLTP databases with that transactional workload. I have added these here briefly, as these concepts will be revisited later in this post in the Lakehouse section.&lt;/p&gt;

&lt;h3&gt;
  
  
  1980 - 2000s: The proliferation of the traditional Data Warehouse.
&lt;/h3&gt;

&lt;p&gt;OLTP databases performed very well in scenarios where fast data retrieval was essential, as well as modifying and deleting operations on a record (row-by-row) basis. Analytical operations, such as aggregation, were considered to be expensive operations. They were expensive because they were data scan-heavy when they did not need to be. Take, for example, if one wanted to know the total revenue from a set of products, they would have to scan every product detail, such as product_name, product_id, product_category, order_number, amount, discount, and quantity. Ideally, to calculate that number, the only necessary fields required are amount * quantity, and product_category, for filtering.&lt;/p&gt;

&lt;p&gt;Between 1980 and 2000, the innovations that helped with this were column-oriented architectures. This architecture allowed data to be stored by columns (fields) and not as rows. Additionally, other data modelling techniques arose. Examples of these data modelling techniques are the Kimball Data Modelling and One Big Table modelling. The column-oriented architecture in conjunction with the new data modelling techniques was found to yield performant results by scanning less I/O, allowing vectorisation and yielding better data compression as more and more data was being collected. Systems that used all these new innovations were termed ‘the data warehouse’. At the time, it was new, but now they are referred to as &lt;strong&gt;the&lt;/strong&gt; &lt;strong&gt;traditional data warehouse&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2003 - 2006: Distributed Systems, MapReduce and cheaper storage
&lt;/h3&gt;

&lt;p&gt;In 2003, the Google File System was developed. The Google File System is a distributed fault-tolerant storage system that runs on commodity hardware, built to meet Google’s increasing and rapidly growing data. The GFS is optimised for handling massive datasets and batch processing. Although it is not really used today and has been replaced by Google’s Colossus, the GFS was pivotal to the big data revolution. The GFS powered Google's search engine, allowing for efficient storage and fast data access. Also, it was able to store multimedia files.&lt;/p&gt;

&lt;p&gt;In 2004, Google developed &lt;a href="https://static.googleusercontent.com/media/research.google.com/en//archive/mapreduce-osdi04.pdf" rel="noopener noreferrer"&gt;MapReduce&lt;/a&gt;, a processing framework for large-scale data. It enables parallel operations on massive datasets by splitting up the data into chunks, mapping tasks to their respective chunks and applying a reduce function to create a final output. These operations are carried out on clusters having a master slave (worker) architecture, with the master coordinating the operations and orchestration shuffle operations, if necessary, among worker nodes. The GFS, being a distributed file system, had a profound impact on the success of MapReduce by providing a reliable and scalable storage infrastructure that enabled data locality and fault tolerance.&lt;/p&gt;

&lt;p&gt;Following GFS and MapReduce was the development of &lt;strong&gt;Hadoop&lt;/strong&gt; in 2006. I have used just the word ‘Hadoop’ intentionally, as it is more like an ecosystem than just one thing. The Hadoop ecosystem consists mainly of 3 components: the Hadoop Distributed File System, MapReduce and YARN (Yet Another Resource Negotiator). Strongly inspired by both the GFS and Google's MapReduce, the HDFS and Hadoop MapReduce were created as open source implementations of these ideas, and YARN was developed to manage cluster resources.&lt;/p&gt;

&lt;p&gt;Before we continue on Hadoop Ecosystem, here is a slight detour on Data Lakes and Variety in Big Data.&lt;/p&gt;

&lt;h3&gt;
  
  
  2010: The Variety Big Data Problem
&lt;/h3&gt;

&lt;p&gt;In the previous sections, the data around a subject was structured, predictable, stayed in the shape almost through out its lifetime, and could be modeled to fit the tabular structure described in the relational model and Kimball data model. Using the relational model and traditional databases and warehouses required designing and carefully modelling the data to accurately describe an entity such that it was still accessible and analysis-ready. These modelling exercises took time and required expertise. One of the outcomes of this data modelling effort was &lt;strong&gt;schemas&lt;/strong&gt;. Also, these schemas had to be known before any data was stored. From a database perspective, this is referred to as schema-on-write.&lt;/p&gt;

&lt;p&gt;There are the V’s of big data. Volume, Variety, Velocity, Veracity and Value. Between 2000 and 2010, the variety of data stored began to shift more from just data that could easily be defined in a tabular structure to non-structured data, leading to the emergence of &lt;strong&gt;data lakes in 2010,&lt;/strong&gt; a term coined by &lt;strong&gt;James Dixon&lt;/strong&gt;. Simply put, A &lt;strong&gt;data lake&lt;/strong&gt; is a central location that holds data in its native, raw format, whether structured or unstructured and at any scale. Data lakes did not need any predefined schema for the data stored. In so doing, data lakes deferred the need for structuring the data until the data was accessed, instead of at the point where the data was stored. When reading the data, the schema was defined, more like inferred, at the point when the data is retrieved, schema-on-read.&lt;/p&gt;

&lt;h3&gt;
  
  
  2006 - 2013: SQL-on-Hadoop - From batch to interactive analysis
&lt;/h3&gt;

&lt;p&gt;Analysts were fond of SQL. SQL had already become very widely used at this time, especially with the proliferation of certain technologies (RDBMS and the data warehouse specifically) that have been discussed in the post. As a result, Hive was developed to cater to the needs of analysts by enabling SQL-like queries on massive datasets on the Hadoop Distributed File System without having to write complex Java programs. It also provided a centralised metadata store for all datasets in what is referred to today as the Hive Metastore. This development began in 2006, and in 2009, Facebook released their paper introducing &lt;a href="https://research.facebook.com/publications/hive-a-warehousing-solution-over-a-map-reduce-framework/" rel="noopener noreferrer"&gt;Hive&lt;/a&gt;. In 2008, Hive was open-sourced, and it became a top-level Apache project. This development brought about SQL-like queries on HDFS, but not interactive analysis like current-day analysts now do, or the typical data warehouses provided.&lt;/p&gt;

&lt;p&gt;While the initial version of Hive allowed for SQL-like operations, these operations were really only suitable for batch operations due to latency. Before going into what technology allowed for interactive analysis in the Hadoop ecosystem, it is worth introducing &lt;strong&gt;Dremel&lt;/strong&gt;. &lt;a href="https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/36632.pdf" rel="noopener noreferrer"&gt;Dremel&lt;/a&gt; revolutionised how SQL operations were run on object storage. It was developed by Google from about 2006 but made public in 2010. Therefore, it can be argued that Dremel pioneered the “interactive era”. It is worth noting that Dremel is sometimes referred to as a query engine only and sometimes as both a query engine and a specific columnar storage format. We will talk a bit more about formats shortly. Dremel is still widely used today as it powers Google's BigQuery.&lt;/p&gt;

&lt;p&gt;As a response to Dremel and strongly inspired by it, many developments have occurred to allow interactive analysis in the Hadoop ecosystem. From 2013-2015, there was a focus on efficiency, leading to the development of &lt;a href="https://tez.apache.org/" rel="noopener noreferrer"&gt;Apache Tez&lt;/a&gt;, replacing the traditional MapReduce. Apache Tez reduced the time for many queries from minutes to seconds. In this same time window, massive parallel processing engines like &lt;a href="https://impala.apache.org/" rel="noopener noreferrer"&gt;Apache Impala&lt;/a&gt; and &lt;a href="https://research.facebook.com/publications/presto-sql-on-everything/" rel="noopener noreferrer"&gt;Presto&lt;/a&gt; (SQL on Everything), now known as Trino, were developed. Trino, today, is one of the core technologies powering AWS Athena.&lt;/p&gt;

&lt;h3&gt;
  
  
  2013 and Ongoing: Columnar file formats on data lakes
&lt;/h3&gt;

&lt;p&gt;As briefly introduced earlier, Dremel is sometimes referred to as a query engine and a nested columnar data format. The data model was essential to the performance gains that Google was able to achieve by eliminating processing overhead. Similarly, other columnar data formats began to emerge. One of them is the very popular &lt;a href="https://parquet.apache.org/" rel="noopener noreferrer"&gt;Apache Parque*&lt;em&gt;t&lt;/em&gt;*&lt;/a&gt;&lt;strong&gt;,&lt;/strong&gt; released in 2013. Parquet is a column-oriented storage format designed by Twitter and Cloudera to improve on Hadoop's existing storage format. To learn more about the Parquet file format, see this &lt;a href="https://vutr.substack.com/p/the-overview-of-parquet-file-format?r=2rj6sg&amp;amp;utm_campaign=post&amp;amp;utm_medium=web&amp;amp;triedRedirect=true" rel="noopener noreferrer"&gt;blog post&lt;/a&gt;. Another known columnar file format is the &lt;a href="https://orc.apache.org/docs/" rel="noopener noreferrer"&gt;ORC (Optimised Row Columnar) file format&lt;/a&gt;, built for the Hadoop Ecosystem too to improve storage and analysis efficiency.&lt;/p&gt;

&lt;p&gt;Today, there are more columnar formats; however, Parquet is very much still widely used. These columnar file formats helped speed up queries, lent themselves excellently to data compression, supported parallel processing, and improved storage efficiency. It also supported schema evolution.&lt;/p&gt;

&lt;h3&gt;
  
  
  2015: Streaming Semantics, Unification of Batch and Stream
&lt;/h3&gt;

&lt;p&gt;It is worth calling out Apache Spark and Apache Flink. Apache Spark was originally developed in 2009 at UC Berkeley's AMPLab, and Flink in 2010 as part of a project named Stratosphere. Data processing can typically be categorised into streaming or batching, and this categorisation usually determines the tool and technologies used. For batch processing, Apache Spark was a go-to tool, while for streaming, Apache Flink was a common choice, and a result, led to separately maintained code bases for batch and stream data processing code. This architecture was referred to as the &lt;a href="https://www.geeksforgeeks.org/system-design/what-is-lambda-architecture-system-design/" rel="noopener noreferrer"&gt;Lambda Architecture&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In 2015, Google published a research paper on &lt;a href="https://research.google/pubs/the-dataflow-model-a-practical-approach-to-balancing-correctness-latency-and-cost-in-massive-scale-unbounded-out-of-order-data-processing/" rel="noopener noreferrer"&gt;the Dataflow Model&lt;/a&gt;. This research provided the theoretical foundation for treating batch and streaming as the same problem. The overarching idea is to treat continuous, messy data as permanently “incomplete” and provide simple building blocks so developers can decide, for each job, how much accuracy, speed, and cost they want, while letting the system handle ordering, windowing, and updates. In the same year, Google released a commercial product offering that was an implementation of their dataflow idea. Conveniently, the product was called Google Cloud Dataflow. In 2016, the Google Cloud Dataflow SDK was made open source and donated to the Apache Foundation; that open source SDK is today known as Apache Beam. This provided a standardised, engine-agnostic way to write unified pipelines that could run on multiple runners like Spark and Flink.&lt;/p&gt;

&lt;p&gt;Within this same period, streaming in Apache Spark became a thing, and Apache Flink programs could be developed to treat batches of data as a finite stream. As of 2018, there was a consolidation at the engine level, integrating for both stream and batch processing using the same internal logic. The unification of batch and stream is crucial for the Modern Data Lakehouse, which will be discussed in a later section in this post.&lt;/p&gt;

&lt;h3&gt;
  
  
  2016 - 2021: Open table formats - ACID on object stores
&lt;/h3&gt;

&lt;p&gt;Remember the columnar file formats, the data lake and the query engines such as Presto? If you do, we can proceed as they are essential for the remaining sections. They are key parts of a system that is being put together in this section of the blog.&lt;/p&gt;

&lt;p&gt;There was still a big challenge about data lakes, and it was maintaining transactional integrity. One thing traditional databases and data warehouses handled well. Other limitations of data lakes were vendor lock-in from different data engine layers, such as Spark, Trino, etc. To address this, Open Table Formats (OTF) were created. Open Table Formats brought database-like ACID operations to Open File Formats in data lakes.&lt;/p&gt;

&lt;p&gt;It is key to distinguish Open File Formats and Open Table Formats. Open File Formats are file formats such as Parquet, while Open Table Formats are, simply put, a metadata layer over an Open File Format. Common Open File Formats are the Apache Hudi, developed by Uber in 2016, Apache Iceberg, developed by Netflix in 2017 and Delta Lake Open File Format, built by Databricks. They are all open-source and continue to be a critical component in the Lakehouse by enabling ACID operations, schema evolution, better partition management, in many cases time travel (point-in-time recovery), and interoperability of the numerous data processing engines mentioned in this blog, such as Spark, Flink, Trino and so on.&lt;/p&gt;

&lt;p&gt;In the next section, we will look at how all this innovation now integrates together to form what is called the Lakehouse.&lt;/p&gt;

&lt;h3&gt;
  
  
  2021 - Ongoing: The LakeHouse
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://www.cidrdb.org/cidr2021/papers/cidr2021_paper17.pdf" rel="noopener noreferrer"&gt;CIDR 2021 Lakehouse paper&lt;/a&gt; argued that implementing warehouse features (ACID tables, schema enforcement/evolution, indexing, cache, governance) on open data lake formats could meet or approach cloud‑warehouse performance without data duplication across lake + warehouse tiers. Lakehouses support SQL analytics &lt;strong&gt;and&lt;/strong&gt; advanced machine learning directly, eliminate multiple ETL steps, reduce lock-in and staleness, and can reach competitive performance using new metadata layers. From the historical developments, one can argue it was the right time for all the technologies to be tightly integrated, such as it formed one big new product. To prove this argument, we look at Databricks Platform. A unified platform using the low-cost storage with an open file format, a metadata layer (delta-lake open file format), a performance layer known as the delta engine, declarative dataFrame APIs for Machine Learning and Data Science and a Multi-API Support Layer. The performance of the entire system was optimal in comparison to other traditional data warehouses using the &lt;a href="https://medium.com/hyrise/a-summary-of-tpc-ds-9fb5e7339a35" rel="noopener noreferrer"&gt;TPC-DS&lt;/a&gt; Power Test, making a very strong case for the viability of the data lakehouse.&lt;/p&gt;

&lt;p&gt;So far, the data lakehouse has had successes for the past few years. There have been other implementations within cloud providers such as the GCP BigLake, AWS CloudFormation, Azure Fabric and so on. Most of the cloud provider options are not natively a lakehouse solution, but they integrate some/most of the components. One major area of the lakehouse that is becoming more robust is the data governance features and capabilities.&lt;/p&gt;

&lt;h3&gt;
  
  
  TL;DR
&lt;/h3&gt;

&lt;p&gt;Over the last 6 decades, analytics and data systems have evolved so much, with so much tooling and technologies. There have been very pivotal moments, such as the relational model and the traditional OLTP databases. In that same period, ACID principles were established to maintain the integrity of the databases. Row-level access was good, but aggregations were expensive; this led to the OLAP data warehouse, optimised for analytical workloads.&lt;/p&gt;

&lt;p&gt;Then came the great decoupling, with the invention of Google File System, MapReduce, and distributed systems. The inseparable components of a once tightly knit data system began to become standalone, robust components. This storage system further allowed for the compute layer to be thought of and built as a separate but integratable component, as seen in Hadoop, Dremel, Presto, etc.&lt;/p&gt;

&lt;p&gt;Now we had storage and compute, but not efficiency. This led to a focus on performance, forming the development of columnar file formats and then the unification of batching and streaming semantics, shifting from the Lambda architecture with the dataflow model, thinking of a single engine, with the internal functions to handle both instead of having separate codebases.&lt;/p&gt;

&lt;p&gt;I consider us to be in a consolidation stage, a stage where the best of all worlds is coming together to form a big, well-thought-out product that has the advanced performance of the tightly knit data warehouses and the flexibility and interoperability that came from the decoupling, as seen in data lakes and the query engines. This consolidation is seen in the Lakehouse.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other Worthy Mentions.
&lt;/h3&gt;

&lt;p&gt;Data Catalogs - Hive Catalog, Unity Catalog, etc.&lt;/p&gt;

&lt;p&gt;Interoperability layers for open file formats - Apache XTable&lt;/p&gt;

&lt;h3&gt;
  
  
  Useful Reads - to be completed
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://medium.com/@himalgodage2759/understanding-mapreduce-googles-revolution-in-data-processing-d95a44bcb289" rel="noopener noreferrer"&gt;Understanding MapReduce&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.onehouse.ai/blog/comprehensive-data-catalog-comparison" rel="noopener noreferrer"&gt;Comprehensive data catalog comparison&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.kimballgroup.com/data-warehouse-business-intelligence-resources/kimball-techniques/dimensional-modeling-techniques/" rel="noopener noreferrer"&gt;Kimball Data Modelling&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dl.acm.org/doi/10.1145/2523616.2523633" rel="noopener noreferrer"&gt;YARN (Yet Another Resource Negotiator).&lt;/a&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>dataengineering</category>
      <category>learning</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Data Compression, Types and Techniques in Big Data</title>
      <dc:creator>Ayokunle Adeniyi</dc:creator>
      <pubDate>Tue, 25 Feb 2025 10:00:00 +0000</pubDate>
      <link>https://forem.com/aws-builders/data-compression-types-and-techniques-in-big-data-pl6</link>
      <guid>https://forem.com/aws-builders/data-compression-types-and-techniques-in-big-data-pl6</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Cover Photo by &lt;a href="https://unsplash.com/@timmossholder?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Tim Mossholder&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/a-large-white-tank-o4mmo5S-55k?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This article will discuss compression in the Big Data context, covering the types and methods of compression. I will also highlight why and when each type and method should be used.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Diving in&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What is compression?
&lt;/h2&gt;

&lt;p&gt;According to the general English definition of compression which refers to reducing something to occupy a smaller space. In Computer Science, compression is the process of reducing data to a smaller size. Data, in this case, could be represented in text, audio, video files etc. Think of it as anything you store on the hard drive of your computer as data represented in different formats. To provide a more technical definition, compression is the process of encoding data to use fewer bits.&lt;/p&gt;

&lt;p&gt;There are multiple reasons to compress data. The most common and intuitive reason is to save storage space. Other reasons are as a result of data being smaller. The benefits of working with smaller data include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Quicker Data Transmission Time: Compressed data are smaller in size and take less time to be transmitted from source to destination.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Reduced bandwidth consumption: This reason is strongly linked to the advantage of quicker data transmission. Compressed data uses less of the network bandwidth, therefore increasing the throughput and reducing the latency.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Improved performance for digital systems that rely heavily on data: This is evident in systems that rely on processing data. Those systems leverage compression to improve the performance of the systems by reducing the volume of data that needs to be processed. Please note that this might be system-specific and will rely on using the appropriate compression technique. Compression techniques will be discussed later in this article.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cost Efficiency: Cloud services charge for the storage of data. By using less storage, cost savings are introduced especially in Big Data systems.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Other reasons for compression depend on different compression techniques and formats. Some encryption algorithms can be used as a method of compression. In doing so, it includes a layer of security to the earlier discussed reasons to compress data. Additionally, using common compression formats brings compatibility and room for extensibility to external systems for integration purposes.&lt;/p&gt;

&lt;p&gt;It is worth noting that the reasons for compression also sound like benefits. However, compression is not without trade-offs. One common trade-off to compression is the need for decompression which might be concerning for resource-constrained systems. Other trade-offs depend on the compression technique and type of data being used.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Systems in the article refer to digital systems that make use of data and can take advantage of compression techniques. The word systems is used quite loosely and should be interpreted in context with what is being discussed in that section.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Compression Types
&lt;/h2&gt;

&lt;p&gt;To discuss the different techniques used to compress data, I will first categorise compression into 2 main categories. This article will then discuss the techniques relevant to each category. Compression can be broadly grouped into &lt;strong&gt;Lossy&lt;/strong&gt; and &lt;strong&gt;Lossless&lt;/strong&gt; compression.&lt;/p&gt;

&lt;p&gt;As the names give away what they mean already, &lt;strong&gt;Lossy compression&lt;/strong&gt; techniques are techniques that do not preserve the full fidelity of the data. Simply put, some data is discarded but not enough to make what the data represents unrecognisable. Hence, lossy compression can offer a very high level of compression compared to lossless compression which will be introduced shortly.&lt;/p&gt;

&lt;p&gt;A characteristic of lossy compression is that it is irreversible, i.e. when presented with the compressed file, one cannot restore the raw data with its original fidelity. Certain files and file formats are suitable for lossy compression. It is typically used for images, audio and videos. For instance, JPEG formatted images lend well to compression and compressing a JPEG image, the creator or editor can choose how much loss to introduce.&lt;/p&gt;

&lt;p&gt;On the other hand, &lt;strong&gt;lossless compression&lt;/strong&gt; is reversible, meaning that when compressed, all data is preserved and restored fully during decompression. This implies that lossless compression is suitable for text-like files, and in the data warehouse and lakehouse world, it would be the only relevant type to use. Some audio (FLAC and ALAC) and image file (GIF, PNG, etc) formats work well with this compression type.&lt;/p&gt;

&lt;h3&gt;
  
  
  Choosing a method
&lt;/h3&gt;

&lt;p&gt;There is no general best compression method. Different factors go into choosing what method would be suitable on a case-by-case basis. To buttress this with examples, a data engineer in the finance industry working on tabular data stored would tend to use lossless compression due to the impact of missing data in creating accurate reporting. Alternatively, lossy compression could be the way to go in optimizing the web page with a lot of images by compressing the images and reducing load items by making the website lighter. Therefore, it is crucial to conduct an assessment to determine the most appropriate compression method that aligns with business requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compression Techniques
&lt;/h2&gt;

&lt;p&gt;This section will only cover the common compression techniques for both lossy and lossless compression. Please note that this is not in any way exhaustive. Furthermore, the techniques discussed may have slight variations to enhance their performance, as backed by different research.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lossless compression techniques
&lt;/h3&gt;

&lt;p&gt;Three common lossless techniques are the Run-Length Encoding (RLE), Huffman Coding and the Lempel-Ziv-Welch techniques.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Run-Length Encoding&lt;/strong&gt;: RLE is based on encoding data, such that, it replaces sequences of repeating data with a single piece of data and the count of that piece of data. It is effective for long runs of repeated data. Also, datasets which have dimensions (fields) that are sorted from a low level to a high level of cardinality benefit from RLE.&lt;/p&gt;

&lt;p&gt;For example, take a simple string like &lt;code&gt;AAAAABBCDDD&lt;/code&gt;. RLE compresses the data to become &lt;code&gt;A(5)B(2)C(1)D(3)&lt;/code&gt;. To be more practical, take a table in the image below.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 1 - before RLE. It is important to observe the level of cardinality is increasing on the fields from left to right&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn72utu9v991ugw21eskk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn72utu9v991ugw21eskk.png" alt="Before RLE - Raw Data" width="800" height="254"&gt;&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 2 - After RLE&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1ezfque0zmaw9jr46ul7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1ezfque0zmaw9jr46ul7.png" alt="After RLE" width="800" height="254"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Because RLE depends on runs of repeated fields and in the second example, the cardinality and the sort order of the data, the &lt;code&gt;Mouse&lt;/code&gt; record on the item column can not be compressed to just &lt;code&gt;Mouse (3)&lt;/code&gt; because the preceding column splits all values into &lt;code&gt;IT, Mouse&lt;/code&gt; and &lt;code&gt;HR, Mouse&lt;/code&gt;. Certain file formats are compatible with RLE such as bitmap file formats like TIFF, BMP etc. Parquet files also support RLE making it very useful in modern data lakehouses using object storage like S3 or GCS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Huffman Coding&lt;/strong&gt;: It is based on statistical modelling that assigns variable length codes to values in the raw data based on the frequency at which they occur in the raw data. The representation of this modelling can be referred to as a Huffman tree, which is, similar to a binary tree. This tree is then used to create a Huffman code for each value in the raw data. The algorithm prioritizes encoding the most frequent values to the fewest possible bits.&lt;/p&gt;

&lt;p&gt;Let's take the same data used in the RLE example &lt;code&gt;AAAAABBCDDD&lt;/code&gt;. The corresponding Huffman tree looks like this.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Huffman Tree&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgrgx2gi0bz0mip7xa67c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgrgx2gi0bz0mip7xa67c.png" alt="Huffman Tree" width="800" height="734"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From the tree, we can see that the letter &lt;code&gt;A&lt;/code&gt; is represented by &lt;code&gt;0&lt;/code&gt; likewise &lt;code&gt;D&lt;/code&gt; is presented by &lt;code&gt;10&lt;/code&gt;. Compared to letters &lt;code&gt;B: 111&lt;/code&gt; and &lt;code&gt;C:110&lt;/code&gt;, we observe that A and D are represented by fewer bits. This is because they have a higher frequency; Hence the Huffman algorithm represents them with fewer bits by design. The resulting compressed data becomes &lt;code&gt;00000111111110101010&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Huffman Coding uses the &lt;strong&gt;prefix rule&lt;/strong&gt; which states that &lt;em&gt;the code representing a character should not be present in the prefix of any other code&lt;/em&gt;. For example, a valid Huffman code can not have letters c and d represented using &lt;code&gt;C: 00&lt;/code&gt; and &lt;code&gt;D: 000&lt;/code&gt; because the representation of &lt;code&gt;C&lt;/code&gt; is a prefix of &lt;code&gt;D&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To see this in action, the Computer Science Field Guide has a &lt;a href="https://www.csfieldguide.org.nz/en/interactives/huffman-tree/" rel="noopener noreferrer"&gt;Huffman Tree Generator&lt;/a&gt; you could play with.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lempel–Ziv–Welch Coding&lt;/strong&gt;: It was created by Abraham Lempel, Jacob Ziv and Terry Welch in 1984 and is named after the creators, obviously 😅. Similar to RLE and Huffman Coding, LZW works well with data that contain lots of repeated data. The LZW algorithm is dictionary-based and creates a dictionary containing key-value pairs of commonly seen patterns in the raw data. Such dictionary can also be referred to as the code table. Using an illustration to explain how this technique works, lets take our raw data to be represented by &lt;code&gt;ABBABABABA&lt;/code&gt;. When passed through the algorithm using a configuration of A-Z as possible values, the resulting code table looks like&lt;/p&gt;

&lt;p&gt;&lt;em&gt;LZW Code Table&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcskdjw7u73re689vlcks.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcskdjw7u73re689vlcks.png" alt="LZW Code Table" width="800" height="228"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From the above code table, there is a key-value pair for all letters A-Z and key-value pairs for patterns such as AB, BB, BA, and ABA. By having a shorter representation of these patterns LZW algorithm can compress the raw by encoding it into fewer bits. Hence, using the code table generated from that input, the compressed version is &lt;code&gt;0 1 1 26 29 28&lt;/code&gt;. It is key to notice the spaces in the compressed data. One could think of them as the end of a character so the decoder will not interpret a &lt;code&gt;1,0&lt;/code&gt; as a &lt;code&gt;10&lt;/code&gt; as they mean different things.&lt;/p&gt;

&lt;p&gt;LZW is usually general-purpose and widely used today. It is integrated into many Unix/Linux-based operation systems behind the &lt;code&gt;compress&lt;/code&gt; shell command. Also, Common file formats compatible with LZW are GIF, TIFF and PDF. Other applications of LZW Compression can be seen in the field of Natural Language Processing as discussed in this paper on &lt;a href="https://arxiv.org/html/2410.21548v1" rel="noopener noreferrer"&gt;tokenization in NLP&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;RLE, Huffman Coding, and LZW Coding are only common examples. Lossless compression techniques go beyond these three (3) described above. Other techniques include &lt;strong&gt;DEFLATE&lt;/strong&gt; which uses a combination of Huffman and LZW - specifically LZ77 - Coding.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lossy compression techniques
&lt;/h3&gt;

&lt;p&gt;In this section, we will look into two types of lossy compression. Recall that lossy compression introduces a loss to the original data, meaning that not all data is kept.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Discrete Cosine Transform (DCT)&lt;/strong&gt;: This compression method is used mainly in audio, image and video files and is also commonly referred to as block compression. It uses a mathematical function - cosine function as the name implies - to convert blocks of the original data into frequencies. The blocks of data are usually a matrix of 8x8, 4x4 and so on, in that order of magnitude.&lt;/p&gt;

&lt;p&gt;The compression comes in when dealing with the high frequencies occurring in the data once the raw data is translated into the frequency domain using the mathematical function. The overall process of using DCT for compression is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Break down raw data into chunks. For instance, in image compression, this could be 8x8 pixels.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Apply the mathematical function to convert the chunks of data to frequencies. This will result in some high frequencies and low frequencies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The high frequencies are then reduced or removed depending on the acceptable degree of loss one is willing to introduce. This is where it really becomes lossy compression.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;To convert back to representable data, all the remaining frequencies are passed through an Inverse Discrete Cosine Transform - IDCT - to restore the data from the frequencies.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;DCT is widely used in different fields today, not only in compression but also in signal processing. Common file formats compatible with DCT are JPEG (images), MP3 (audio), and MPEG (video). Additionally, DCT can achieve high compression ratios making it suitable for digital systems with lots of images like web pages on the Internet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fractal Compression&lt;/strong&gt;: A fractal is a self-repeating infinite pattern that repeats at different scales. When viewed from any point on the scale, the pattern looks similar. Because the patterns are similar at any scale, fractal compression reduces the scale of 'big' fractals to reduce the size of the data.&lt;/p&gt;

&lt;p&gt;Examples of Fractals&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv26xssh8nnw89hcfkf1p.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv26xssh8nnw89hcfkf1p.PNG" alt="Fractal Example" width="640" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fractal Compression was introduced by Michael Barnsley in the 1980s. The general idea using an image is that if an image contains several parts that look alike, why store them twice? To do this, fractal compression does the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Partitions the image into non-overlapping blocks known as &lt;em&gt;range blocks&lt;/em&gt;. This could be range blocks of 8x8, 16x16 pixels, etc.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It scans the image for self-repeating patterns (fractal patterns). Using the range blocks, the algorithm finds larger sections of the image that are similar to these range blocks. These larger sections are referred to as the &lt;em&gt;domain blocks&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A transform function is then applied to the domain block to approximate the range blocks. These transform functions are mathematical functions such as scaling, translation, rotation etc. They can also be referred to as transformations. These transformations are called &lt;em&gt;fractal codes&lt;/em&gt; with respect to Fractal Compression.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The data is then encoded to those transform functions. Instead of storing the pixel-pixel data, the transformations are stored. These transformations are the rules that describe how to reconstruct the image from domain blocks.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With the fractal codes, the image is reconstructed using an iterative process. This process can be computationally expensive but fractal compression could achieve a high ratio of compression compared to other compression techniques. Due to its reliance on self-repeating patterns, it would perform better on data that conforms to having such self-repeating patterns. Examples would be landscape photographs (images of nature) and DNA images.&lt;/p&gt;

&lt;p&gt;There are other lossy compression techniques such as Discrete Wavelet Transform, Quantization. These techniques are usually used in images, audio and video files and are suitable for certain types or file formats - JPEG, MP3 - for each file type. &lt;/p&gt;

&lt;p&gt;Lossy compression generally has higher compression ratios than lossless compression and sometimes expects that the user knows the amount of loss to introduce beforehand. it is pertinent to emphasise that the choice of compression method and technique depends on several factors. At the core of these factors are the data format and the desired outcome.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Overall, this post discusses compression in the world of data. It relies strongly on the existing body of knowledge in computer science and information theory. To compress means to reduce the volume an entity occupies and in the field of data, volume refers to storage space. Compression in digital systems has many advantages when done right. The obvious is that it reduces the space and gives room to store more data. Other advantages include quicker transmission, lesser bandwidth usage and general improvement in the efficiency of said system. Remember, this is when it is done right.&lt;/p&gt;

&lt;p&gt;To leverage the advantages of compression, it is key to know what type to use. Compression is either lossy or lossless. Lossy compression introduces a loss in the original data that is usually irreversible while lossless compression compresses the data and retains all the information contained in the original data. Furthermore, there is discourse on hybrid compression types but I think a combination of lossy and lossless is just lossy. Let me know what you think in the comments.&lt;/p&gt;

&lt;p&gt;Lastly, different techniques were introduced for both lossy and lossless compression. The list of techniques and explanations of these techniques are neither exhaustive nor comprehensive. I consider them only a good start in giving you an idea of how each technique works. To wrap up, I have added additional resources to help you investigate further and read deeper about compression in big data.&lt;/p&gt;




&lt;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://youtu.be/hFFP2OYFlTA?t=303" rel="noopener noreferrer"&gt;Video: Data Lake fundamentals - RLE encoding with Parquet in practice&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.researchgate.net/publication/322557949_A_review_of_data_compression_techniques" rel="noopener noreferrer"&gt;Paper: A review of data compression techniques&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ieeexplore.ieee.org/document/8229810" rel="noopener noreferrer"&gt;Paper: lossless compression techniques&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://books.google.com/books?hl=en&amp;amp;lr=&amp;amp;id=mnpeizY0btYC&amp;amp;oi=fnd&amp;amp;pg=PA2&amp;amp;dq=Data+compression+papers&amp;amp;ots=zvCCDiJ0In&amp;amp;sig=2XCl9MbqOOGbgTa49a8unYwAkJM" rel="noopener noreferrer"&gt;A concise introduction to Data Compression by David Salomon&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.csjournals.com/IJCSC/PDF6-2/12.%20Ravi.pdf" rel="noopener noreferrer"&gt;Paper: A Study of Various Data Compression Techniques&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/towards-data-engineering/advanced-file-formats-and-compression-techniques-55c7c7c1a396" rel="noopener noreferrer"&gt;Blog Post: Compression in open file formats&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.starburst.io/data-glossary/open-file-formats/" rel="noopener noreferrer"&gt;Article: Open file formats&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://risingwave.com/blog/understanding-database-compression-techniques/" rel="noopener noreferrer"&gt;Article: Compression in databases&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://bmcbioinformatics.biomedcentral.com/articles/10.1186/s12859-020-03658-4" rel="noopener noreferrer"&gt;Lossy Compression for Genomic data (RNA)&lt;/a&gt;&lt;/p&gt;

</description>
      <category>datascience</category>
      <category>data</category>
      <category>learning</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Custom Data Types in SQL</title>
      <dc:creator>Ayokunle Adeniyi</dc:creator>
      <pubDate>Tue, 04 Feb 2025 09:05:00 +0000</pubDate>
      <link>https://forem.com/aws-builders/custom-data-types-94</link>
      <guid>https://forem.com/aws-builders/custom-data-types-94</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Cover Photo by &lt;a href="https://unsplash.com/@xavi_cabrera?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Xavi Cabrera&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/yellow-red-blue-and-green-lego-blocks-kn-UmDZQDjM?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Before, we dive in, this post focuses on Oracle databases. PL SQL is a strongly typed language. For every variable used in a program or subprogram, the variable must be declared with a data type. PL SQL comes with some data types already defined. Examples of these data types are VARCHAR, VARCHAR2, NUMBER, DATE, etc., and so on. Generally, data types can be grouped into 4 main buckets which are scalars, LOB (Large objects) types, reference types, and composite data types. Scalars are atomic data types such as NUMBER, BOOLEAN, VARCHAR, and many more while composite data types consist of one or more scalars. Examples of composite data types are record types, collection types, and object types.&lt;/p&gt;

&lt;p&gt;Think about small pieces of Legos coming together much like a puzzle to build a Lego Spider-Man. The same can be said about data types in databases. Although the inbuilt data types may not always be well suited to your needs, a combination of multiple data types could be used to fit into the puzzle or, in this case, your application. Furthermore, in SQL (PL SQL in this case), Oracle allows you to create custom data types that other programs and subprograms can use in the database. These are typically what composite data types are.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This article will focus mainly on scalars and composite data types. Let's dive in&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Scalars
&lt;/h3&gt;

&lt;p&gt;You can not create your own scalar because scalars are base types but you can create a subtype. Subtypes do not introduce a new type. They, however, place optional constraints on a base type. Generally, subtypes improve the readability of your code by indicating the intended use of the variable, for instance, a user-defined currency subtype will indicate that the variable is going to be used for finance-related activities. Subtypes also improve reliability by making use of the constraints.&lt;/p&gt;

&lt;p&gt;Subtypes are defined in the declarative part of any PL SQL block, subprogram, or package using the syntax below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* syntax to define subtypes */

SUBTYPE subtype_name IS base_type[(constraint)] [NOT NULL];

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Examples of subtypes can be seen in the code snippet below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DECLARE

    /* Number must be 9 digits */
    SUBTYPE T_NatIDNum IS PLS_INTEGER RANGE 100000000 .. 999999999; 
    SUBTYPE T_BirthDate IS DATE;

    /* Numbers will have maximum precision of 2 decimal places */
    SUBTYPE T_Height_weight IS NUMBER(10,2); 

    v_b_date T_BirthDate;
    v_height T_Height_weight;
    v_weight T_Height_weight;
    v_nat_id_number T_NatIDNum;

BEGIN

......

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From the above snippet, the v_height and v_weight variable can only have a maximum precision of 2 decimal places while the v_nat_id_number variable must be between 100000000 and 999999999.&lt;/p&gt;

&lt;h2&gt;
  
  
  Composite Data Types
&lt;/h2&gt;

&lt;p&gt;Composite data types (also known as user-defined types) can be created by a user. These composite data types can usually take 3 forms which are record types, object types, and collection types. All composite data types have internal components. These internal components could either be a scalar or another composite data type. Internal components can be accessed in a composite data type and this is done usually using the dot notation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Record Types
&lt;/h3&gt;

&lt;p&gt;It is similar to a row in a database table. its internal components can be of different data types and are referred to as fields. The simple snippet below shows how a record type is declared and used in a subprogram. The internal components are accessed using the dot notation as indicated in the executable part.&lt;/p&gt;

&lt;p&gt;Note: Record types are usually used and declared in packages and subprograms and are usually not preceded with the CREATE keyword as shown in the example below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DECLARE
    /* Record Type declaration */
    TYPE emp_contact IS RECORD (
        /* internal components */
        emp_id hr.employees.employee_id%TYPE,
        emp_email hr.employees.email%TYPE,
        emp_phone_no hr.employees.phone_number%TYPE
    );

    /* Variable declaration */
    v_emp_contact EMP_CONTACT;

BEGIN
    SELECT employee_id, email, phone_number INTO v_emp_contact 
    FROM hr.employees
    WHERE employee_id = 101;

    DBMS_OUTPUT.PUT_LINE('Employee ID: ' || TO_CHAR(v_emp_contact.emp_id)); 
    DBMS_OUTPUT.PUT_LINE('Employee Email: ' || LOWER(v_emp_contact.emp_email) || '@learnplsql.com'); 
    DBMS_OUTPUT.PUT_LINE('Employee Phone Number: ' || TO_CHAR(v_emp_contact.emp_phone_no)); 

END;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Object types
&lt;/h3&gt;

&lt;p&gt;These allow you to create abstractions of real-world objects just like other object-oriented programming languages. Object types can have 3 components&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Attributes: These can be user-defined types or the default scalars. They structure the object&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Name: This is the name of the object. It must be unique in the schema.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Methods: Methods are functions or procedures that model the behavior of an object just like a real-world entity. They are usually preceded with the keyword &lt;strong&gt;MEMBER&lt;/strong&gt; when specified as a component of the object. Methods can also be preceded with keywords &lt;strong&gt;STATIC&lt;/strong&gt; or &lt;strong&gt;COMPARISON&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;*NOTE: Methods preceded with the MEMBER keyword have an implicit SELF parameter as the first parameter *&lt;/p&gt;

&lt;p&gt;Objects types are created as stand-alone objects and can be used just like the built-in data types. Object types may require a body just like packages in PLSQL if the object has methods. Let's define 2 objects to model external parties in a company (visitors and vendors). The visitor object will be very simple while the vendor object will have methods defined as components.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* Visitor object */
CREATE OR REPLACE TYPE obj_visitor AS OBJECT (
    visitor_id   NUMBER(4),
    first_name   VARCHAR2(30),
    last_name    VARCHAR2(30),
    whom_to_see  VARCHAR2(50)
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While for the vendor object we are going to create, we have&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* Vendor Object */

/* object type specification */
CREATE OR REPLACE TYPE obj_vendor AS OBJECT (
    visitor_id NUMBER(4),
    first_name VARCHAR2(30),
    last_name VARCHAR2(30),
    whom_to_see VARCHAR2(50),
    company VARCHAR2(40),

    /* Constructor Method [optional] */ 
    CONSTRUCTOR FUNCTION obj_vendor(visitor_id NUMBER, first_name VARCHAR2, last_name VARCHAR2, company VARCHAR2) RETURN SELF AS RESULT,

    /* Other methods */
    MEMBER PROCEDURE insert_vendor (SELF IN OUT NOCOPY obj_vendor), 
    MEMBER FUNCTION display_vendor_details RETURN VARCHAR2)


/* object type body */
CREATE OR REPLACE TYPE BODY obj_vendor AS
    CONSTRUCTOR FUNCTION obj_vendor (
        visitor_id  NUMBER,
        first_name  VARCHAR2,
        last_name   VARCHAR2,
        company     VARCHAR2
    ) RETURN SELF AS RESULT AS
    BEGIN
        dbms_output.put_line('object constructor function fired ==&amp;gt;');
        self.visitor_id := visitor_id;
        self.first_name := first_name;
        self.last_name := last_name;
        self.company := company;
        RETURN;
    END;

    MEMBER PROCEDURE insert_vendor (
        self IN OUT NOCOPY obj_vendor
    ) AS
    BEGIN
        INSERT INTO visitors VALUES (
            visitor_id,
            upper(first_name),
            upper(last_name),
            upper(company),
            sysdate
        );

        COMMIT;
    END;

    MEMBER FUNCTION display_vendor_details RETURN VARCHAR2 AS
        v_details VARCHAR2(300);
    BEGIN
        --dbms_output.put_line('Vendor visitor details are');
        --dbms_output.put_line('Visitor ID: ' || to_char(visitor_id));
        --dbms_output.put_line('Visitor Name: ' || first_name || ' ' || last_name);

        v_details := 'Vendor with visitor ID: '
                     || TO_CHAR(visitor_id)
                     || ' and Fullname: '
                     || INITCAP(first_name)
                     || ' '
                     || INITCAP(last_name)
                     || ' from '
                     || UPPER(company)
                     || ' company.';

        RETURN v_details;
    END;

END;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: For member procedures, you can either pass parameters like regular procedures with IN, OUT keywords. if the SELF keyword is not passed, the parameter mode will default to the IN OUT configuration. However, for performance reasons, you can use the SELF IN OUT NOCOPY . You can read more on this &lt;a href="https://docs.oracle.com/en/database/oracle/oracle-database/18/adobj/design-considerations-for-methods.html#GUID-D9E253CB-59F5-4517-82E8-AD71E2C6F6CC" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So now that we have created two (2) objects. Let's use them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DECLARE
    v_vendor       obj_vendor; /* object is automatically null at this point */
    v_vendor_info  VARCHAR2(200);
BEGIN

    /* Instantiating the object and invoking the constructor */
    v_vendor := obj_vendor(v_id_seq.nextval, 'john', 'doe', 'pl/sql academy');

    /* Manually displaying the vendor ID */
    dbms_output.put_line('Vendor id is: ' || to_char(v_vendor.visitor_id));

    /* Calling the member method (function) */
    v_vendor_info := v_vendor.display_vendor_details();
    dbms_output.put_line(v_vendor_info);

    /* Calling the member method (procedure) */
    v_vendor.insert_vendor();
END;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Collection types
&lt;/h3&gt;

&lt;p&gt;In a very simplified explanation, collections are basically arrays in PL SQL. There are 3 types: associative arrays, variable-sized arrays (VARRAYS), and nested tables. The sample below shows a simple associative array. Arrays have built-in methods like &lt;strong&gt;COUNT&lt;/strong&gt; to get the length of the array. A comprehensive list of other methods can be found &lt;a href="https://docs.oracle.com/database/121/LNPLS/composites.htm#GUID-0452FBDC-D9C1-486E-B432-49AF84743A9F" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Elements in an array can be accessed using their indexes starting from 1 instead of 0 in scripting languages like python, etc.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DECLARE
    TYPE t_emp_info IS RECORD (
        emp_f_name  hr.employees.first_name%TYPE,
        emp_l_name  hr.employees.last_name%TYPE,
        emp_dept    hr.departments.department_name%TYPE
    );

    /* associative array type (collection)*/
    TYPE t_emp_array IS
        TABLE OF t_emp_info INDEX BY PLS_INTEGER;

    /* variable of collection type */
    v_emp_array      t_emp_array;

    /* misc */
    v_first_element  t_emp_info;
    v_last_element   t_emp_info;
    v_new_rec        t_emp_info;
    v_arr_length     NUMBER(5);
BEGIN
    SELECT
        first_name,
        last_name,
        department_name
    BULK COLLECT
    INTO v_emp_array
    FROM
        hr.employees      e,
        hr.departments    d
    WHERE
        e.department_id = d.department_id
    ORDER BY
        e.employee_id;

    v_arr_length := v_emp_array.count;
    dbms_output.put_line('Collection length: ' || to_char(v_arr_length));
    v_first_element := v_emp_array(1);
    dbms_output.put_line('First element: ' || v_first_element.emp_f_name);
    v_last_element := v_emp_array(v_arr_length);
    dbms_output.put_line('Last element: ' || v_last_element.emp_f_name);

    /* delete first element */
    v_emp_array.DELETE(1);

    /* First element becomes empty */
    BEGIN
        dbms_output.put_line('New First element: ' || v_emp_array(1).emp_f_name);
    EXCEPTION
        WHEN no_data_found THEN
            dbms_output.put_line('Element is null');
    END;

    /* Re assign first element */
    v_new_rec.emp_f_name := 'Jane';
    v_new_rec.emp_l_name := 'Doe';
    v_new_rec.emp_dept := 'Special-Ops';
    v_emp_array(1) := v_new_rec;

    /* print new first record */
    dbms_output.put_line('New First element: ' || v_emp_array(1).emp_f_name || ' of ' || v_emp_array(1).emp_dept || ' department.');
END;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output of the above PL SQL blocks returns&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkq0183ard7cpizg1mpe4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkq0183ard7cpizg1mpe4.png" alt="Image description" width="800" height="239"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Composite data types can be fun and handy. Although it may not be very common to see developers use objects and OOP in PL SQL, records and collections could be instrumental in making your code more optimal, better performing, and neater to read.&lt;/p&gt;

&lt;p&gt;I hope it is pretty clear what custom data types are in PL SQL and what you can do with them. Don't forget that practice makes better and you can use the live SQL platform &lt;a href="https://livesql.oracle.com" rel="noopener noreferrer"&gt;here&lt;/a&gt; to start practicing right away.&lt;/p&gt;

</description>
      <category>oracle</category>
      <category>sql</category>
      <category>data</category>
      <category>learning</category>
    </item>
    <item>
      <title>Understanding Database Indexes And Their Data Structures: Hashes, SS-Tables, LSM Trees And B-Trees</title>
      <dc:creator>Ayokunle Adeniyi</dc:creator>
      <pubDate>Tue, 05 Mar 2024 09:01:00 +0000</pubDate>
      <link>https://forem.com/aws-builders/understanding-database-indexes-and-their-data-structures-hashes-ss-tables-lsm-trees-and-b-trees-2dk5</link>
      <guid>https://forem.com/aws-builders/understanding-database-indexes-and-their-data-structures-hashes-ss-tables-lsm-trees-and-b-trees-2dk5</guid>
      <description>&lt;p&gt;There's often a huge fuss about making data-driven decisions, leveraging data analytics, using data science and data-centred thinking. From a technological point of view, data is usually stored and accessed using databases. Databases, often referred to as RDBMS, are sophisticated systems that abstract away the fairly complex logic and engine behind data storage on disk. Several databases exist in today's tech landscape, but this article will be focused on something common to data storage and retrieval. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Drum roll 🥁🥁🥁🥁🥁🥁🥁. We will be discussing indexes. The topic gives it away anyway.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Log-based Data Structures
&lt;/h2&gt;

&lt;p&gt;My approach draws a slightly historical perspective to database indexes. To begin, we look at the simplest form of a database - A file. Think of this file as a &lt;em&gt;log&lt;/em&gt; file. Every time we need to store stuff, we append the data to the log file. To retrieve the data, we traverse each entry till we get the information we want. &lt;/p&gt;

&lt;p&gt;The above illustration sounds straightforward and is very efficient for storing data (write operations) because all it needs to do is &lt;em&gt;an append&lt;/em&gt; operation. However, it introduces a huge challenge to retrieve data (read operations) when the data grows in volume. This is because the program has to go through all entries all the time. In computer science, the big-O notation for this sort of operation is O(n), n being the number of records. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For clarity, we can think of each entry as having a key and value. The indexes in this article will refer to the key in each entry.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is where indexes come in. Imagine if we had a separate data structure that tells us where the information is. something like the famous &lt;strong&gt;indices&lt;/strong&gt; at the back of the Oxford Dictionary. An index in this context is a data structure that is derived from the primary data that helps retrieve information quickly. There are multiple variations of indexes and we will get into them in the next sections&lt;/p&gt;

&lt;h3&gt;
  
  
  Hash indexes
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;A hash index is represented using a similar data structure to the dictionary data type in Python or a hashMap in Java.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's refer back to our simple example database where we have multiple entries appended to a log-based file. A hash index would be akin to having an in-memory key-value pair of every key in the data appended and its byte offset in the data file. In so doing, when we look up some data, we search the index for the key and find a pointer to the location of the actual values on the data file. Also, every time a value is added to the database, the hash index is updated to include that new entry using its key.&lt;/p&gt;

&lt;p&gt;Before going into the pros and cons of hash indexes, I will build on the illustration of this database. since the existing database is a log-based database (append-only), when an already existing value is to be updated, the database does not seek for the data and modify the values in place. Rather, it appends the entire entry and read operations are built to make sure to get the latest value for any given key. This bolsters the need for an index because the hash map will also be updated to point to the most recent location for that entry. &lt;/p&gt;

&lt;p&gt;Assuming we have a service that is transactional in nature. This implies that new entries will be added and existing entries will be changed frequently. For entirely new entries, this is no problem. However, every change to an existing entry will append a new entry and will make the older values redundant. If we use hash indexes to solve the issues of long lookup times, reclaiming disk space used by those redundant values in the database is still a significant challenge. &lt;/p&gt;

&lt;p&gt;But how do we prevent the disk from running out of space? A simple solution to this is to split the log files. Each split can be referred to as a &lt;em&gt;segment&lt;/em&gt;. After splitting the files into segments, a &lt;em&gt;&lt;strong&gt;compaction&lt;/strong&gt;&lt;/em&gt; process can be run in the background. This &lt;em&gt;compaction&lt;/em&gt; takes a segment or multiple segments and merges them. In this process, only the latest values are kept and others are discarded and written into a new segment. After which, new operations are redirected from the older segments to the newly compact segments. Note that the database is still split into files but compaction reduces the amount of redundant keys in the database.&lt;/p&gt;

&lt;p&gt;In relation to hash indexes, each segment will have its own in-memory hash maps, and these are also updated after merging and compaction also. When a lookup operation is done, it first checks the hash map of the most recent segment, then traverses backwards to the next most recent and the next and so on.&lt;/p&gt;

&lt;h4&gt;
  
  
  Limitations of hash indexes
&lt;/h4&gt;

&lt;p&gt;In practice, log-based databases and hash indexes are very efficient but still have limitations. Some of the core limitations of the example above are poor concurrency control, crash recovery, partial writes, no support for delete operations and range queries are inefficient. Because the index is in-memory (RAM), if the server is restarted, all hash maps are lost. Additionally, the entire hash index must fit in memory. These limitations do not fit the requirements for how we interact with databases and the deluge of data we work with today.&lt;/p&gt;

&lt;p&gt;To address these limitations, enhancements and changes are made to the existing data structure housing the hash map indexes per segment. Referring back to the current state of the database, we recall that we now have our data split into different segments and segments undergo compaction. &lt;/p&gt;

&lt;h3&gt;
  
  
  SSTables
&lt;/h3&gt;

&lt;p&gt;We make a fairly simple change to how the data is stored in these segment files. The change we introduce is to sort the &lt;em&gt;&lt;u&gt;data (key-value) by the key&lt;/u&gt;&lt;/em&gt;. By doing this, the data is stored in a sorted format on disk using the keys. This is referred to as a &lt;strong&gt;Sorted String Table (SSTable)&lt;/strong&gt;. The obvious limitation it solves now when compared to plain hash maps is that we can now fully support range queries. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The term was coined in &lt;a href="https://research.google/pubs/bigtable-a-distributed-storage-system-for-structured-data/"&gt;Google's Bigtable paper&lt;/a&gt;, along with the term &lt;em&gt;memtable&lt;/em&gt;. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In comparison to log-based storage with hash indexes, SSTables introduce an additional layer to &lt;em&gt;merging&lt;/em&gt; and &lt;em&gt;compaction&lt;/em&gt;. What it does is use an algorithm very similar to the popular &lt;strong&gt;&lt;em&gt;mergesort&lt;/em&gt;&lt;/strong&gt; algorithm to maintain the order of all entries in the SSTable&lt;/p&gt;

&lt;p&gt;It is worth noting that sometimes, SSTables are not referred to as indexes but as a data structure itself, which seems to be a better description of what an SSTable is. In this case, the accompanying index can be referred to as an SSIndex or SSTable index or Memtable. However, for the purpose of this article, SSTable will refer to the combination of both the datafile (sorted key-value pairs on disk) and its corresponding in-memory index containing the keys and their bytes offset.&lt;/p&gt;

&lt;h4&gt;
  
  
  SSTables vs Hash Indexes
&lt;/h4&gt;

&lt;p&gt;All the advantages of hash indexes are preserved in SSTables. That is, &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it is still efficient for write operations since it is log-based (append-only)&lt;/li&gt;
&lt;li&gt;the in-memory index will act as a pointer to the actual location of the data on the disk &lt;/li&gt;
&lt;li&gt;The &lt;em&gt;compaction&lt;/em&gt; process in the background makes it efficient from a storage perspective&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Key Advantages
&lt;/h5&gt;

&lt;p&gt;The additional advantage when compared to Hash indexes are in 2 folds. First, we can now &lt;em&gt;&lt;u&gt;query ranges&lt;/u&gt;&lt;/em&gt; because our data is sorted by the key. Second, the index can be &lt;em&gt;&lt;u&gt;sparse&lt;/u&gt;&lt;/em&gt;. To explain the second point, let's use the example that we have keys from A0 to A1000. The index will not need to have 1000 keys but can have half of that which is 500 keys. So the keys could be A0, A2, A4, A6 and so on with their corresponding pointers to their location on the disk. When trying to retrieve the values associated with key A5, even though it is not in the index, we know that it is in between A4 and A6 and we can begin our search from there. Thus, making the index sparse without trading off read performance.&lt;/p&gt;

&lt;h5&gt;
  
  
  Drawbacks of SSTables
&lt;/h5&gt;

&lt;p&gt;However, it is not without its limitations. The entire index still needs to fit within the memory of the server. If the server crashes, the index is lost. In a very busy transactional database, that is a lot of work required to keep the SSTable up to date. In the next section, we continue to build on our knowledge of log-based databases and indexes with LSM Trees.&lt;/p&gt;

&lt;h3&gt;
  
  
  Log-Structured Merge (LSM) Trees
&lt;/h3&gt;

&lt;p&gt;We have established that log-based approaches to data storage can be very efficient. Just like the SSTables, LSM Trees are also log-based in their way of storing data on disk and have an in-memory data structure akin to &lt;em&gt;Memtable&lt;/em&gt; in SSTrees. In fact, LSM Trees make use of SSTrees.&lt;/p&gt;

&lt;p&gt;LSM Trees are layered collections of Memtables and SSTables. The first layer is the &lt;em&gt;memtable&lt;/em&gt; stored in memory. The following layers are cascaded SSTables and these layers are stored on disk. Its major characteristic can be observed in how it handles write operations. During the write operations, the entries are initially added to the memtable and are then flushed to SSTables after an interval or when it reaches a certain size. This mechanism makes writes very fast but can slow up reads. &lt;/p&gt;

&lt;h4&gt;
  
  
  Implication on read and write operations
&lt;/h4&gt;

&lt;p&gt;Reads have to look up the key in the &lt;em&gt;memtable&lt;/em&gt; first for the most recent entries, then traverse through the layers of SSTables. Therefore, it is a painful operation to look up a key that does not exist in the LSM tree because it ends up searching through all the layers of data available. &lt;em&gt;&lt;u&gt;Bloom filters&lt;/u&gt;&lt;/em&gt; help mitigate and optimize these experiences by being able to quickly determine if a key might exist in the SSTable.&lt;/p&gt;

&lt;p&gt;Overall, LSM Trees seem to provide superior performance when it comes to workloads that involve a lot of write operations while SSTables are preferable when quicker reads are essential.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Databases like CassandraDB, LevelDB use LSM Trees.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To address the limitation of preserving the indexes when the server crashes, it sounds intuitive to store the index on disk as it is smaller in size. Since it is smaller, it should be easier to read and update. However, that is not the case because of how storage disks are designed (SSDs and HDDs). In practice, what is done is that the index has a file that is read into memory when the database server is back up and running. Furthermore, for every write operation the key is added into the &lt;em&gt;&lt;strong&gt;memtable&lt;/strong&gt;&lt;/em&gt; - remember the &lt;em&gt;memtable&lt;/em&gt; is the in-memory data structure - and a &lt;em&gt;&lt;strong&gt;Write Ahead Log (WAL)&lt;/strong&gt;&lt;/em&gt; which is persistent on disk. Recall that write operations are very efficient. Therefore, the essence of having a persistent WAL is so that in the event of a server crash, the WAL has all that is required to rebuild the in-memory index. This WAL can be applied to both the SSTable, LSM Trees and other indexing strategies.&lt;/p&gt;

&lt;h2&gt;
  
  
  B-Trees
&lt;/h2&gt;

&lt;p&gt;All the indexes and data structures discussed above have something in common, which is they are all log-based. Here, I discuss a very popular and mature data structure that is widely used in the most popular databases today. B-Trees are data structures that keep and maintain sorted data such that they allow searches and sequential access to the data in logarithmic time. They are self-balancing and very similar in nature to a binary search tree, if you are familiar with programming. &lt;/p&gt;

&lt;p&gt;They are tree-like and essentially break down the database into fixed-sized blocks referred to as &lt;em&gt;&lt;strong&gt;pages&lt;/strong&gt;&lt;/em&gt;. These pages are commonly 4KB in size by default, although, many RDBMS systems offer the option to change the page size. In comparison to the log-based which uses &lt;em&gt;segments&lt;/em&gt; where data is append-only, B-Trees allows us to access and manipulate the data in place on the disk using references to those &lt;em&gt;pages&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;Because it is tree-like, it has one root, inner and leaf nodes. One node is designated to the root of the B-Tree and has pointers to children nodes. Every time a lookup is done using the key, you have to start there and traverse through different hierarchies to the key being looked for, guaranteeing access in logarithmic time. &lt;/p&gt;

&lt;p&gt;Remember, we made the assumption that our data entries are key-value pairs. Therefore, it is worth noting that out of all the 3 types of nodes - root, inner and leaf - only the leaf nodes contain the actual information (values), the others only contain references to other nodes and so on till they point to the corresponding leaf node. Additionally, within the tree hierarchy, the outermost child references indicate the boundaries in that range. &lt;/p&gt;

&lt;p&gt;When a write operation (insert or update in SQL terminology) is done, the idea is to locate the page where the values should be on disk and write the value to that page. Therefore, we must consider what happens when a page becomes full. In this case, a split operation occurs, The page is split into 2 and all cascading nodes above must be adequately updated to reflect the change that has occurred.&lt;/p&gt;

&lt;p&gt;B-Trees have depth and width that are inversely proportional to one another. That is, the deeper the B-Tree the slimmer it is. The technical term for the width is referred to as the &lt;em&gt;&lt;strong&gt;branching factor&lt;/strong&gt;&lt;/em&gt;, which is defined as the number of references to child nodes within a single node. Linking back to a write operation that may cause a page to split, this will, in turn, require several updates if the B-Tree has a lot of depth. Additionally, careful measures must be put in place to protect the tree's data structure during split and concurrent operations, and to achieve this, &lt;em&gt;internal locks (latches)&lt;/em&gt; are placed for the time they are being updated.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;B-Trees are not log-based but they also use Write-Ahead logs to recover from crashes and for redundancy.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  B-Trees in comparison to LSM Trees
&lt;/h3&gt;

&lt;p&gt;Writes are slower in B-Trees in comparison to LSM Trees. On the other hand, reads are faster when using B-Trees. It is, however, important to experiment and test extensively for any use case. Benchmarking is essential when choosing the database and indexing strategy that would best support your workload. &lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Each data structure supporting different indexes has its strong points as well as areas of weakness. In this article, we have discussed 4 main data structures that power indexes. These are hash indexes, SSTables, LSM Trees and B-Trees. The first 3 have a log-based data structure, such that, they are append-only. Their respective limitations were mentioned, and how some address certain limitations, for instance, SSTables support range queries because the data is sorted. Some general optimizations such as the use of a Write Ahead Log for crash recovery, compaction and merging for saving disk space, and latches for concurrency control. Lastly, we briefly compared the performance of different pairs of data structures at the tail end of each section.&lt;/p&gt;

&lt;p&gt;This article is strongly inspired by my current read: &lt;em&gt;&lt;a href="https://www.amazon.co.uk/Designing-Data-Intensive-Applications-Reliable-Maintainable/dp/1449373321"&gt;Designing Data-Intensive Applications by Martin Kleppmann&lt;/a&gt;&lt;/em&gt;. I completely recommend it if you want to broaden your understanding of data systems. Please share in the comment sections interesting books, articles, and posts that have inspired and helped you understand a concept better.&lt;/p&gt;

</description>
      <category>database</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Functions in SQL</title>
      <dc:creator>Ayokunle Adeniyi</dc:creator>
      <pubDate>Tue, 27 Feb 2024 09:01:00 +0000</pubDate>
      <link>https://forem.com/ayokunle/functions-in-sql-179h</link>
      <guid>https://forem.com/ayokunle/functions-in-sql-179h</guid>
      <description>&lt;p&gt;Functions are very similar to procedures in databases. In this article, I will try to break down functions in SQL and also mention the differences between functions and procedures in a database.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Remember! The RDBMS used in this article is the Oracle database. Let's dive in!!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The common differences between functions and procedures are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A function is a block of code called to perform a task and must return one or more values while a procedure is a block of code called to perform a task. Procedures do not have to return a value.&lt;/li&gt;
&lt;li&gt;A function can be called in a procedure but a procedure cannot be called in a function.&lt;/li&gt;
&lt;li&gt;A function can be called in a select statement but it must not contain IN or IN OUT parameters in the case while procedures cannot be executed in a select statement.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In PL SQL (Oracle), there are two (2) types of functions, we have the:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In-built functions: these are functions that come with the database at installation. An example of an in-built function is the NVL (used to handle null values) function
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT FIRST_NAME, NVL(HAS_SIBLINGS, 0) FROM STUDENTS;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;The sample code above returns zero (0) where the &lt;code&gt;HAS_SIBLINGS&lt;/code&gt; column has a &lt;code&gt;null&lt;/code&gt; value.&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User-defined functions: As the name implies, these are functions created by developers and users of the database.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Just like procedures, functions have two (2) main parts, the header and the body. The header has the name of the function and a RETURN clause specifying the data type to be returned by the function. The header looks something like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- header

CREATE OR REPLACE FUNCTION get_emp_email(v_id IN NUMBER)
RETURN VARCHAR2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function body has three (3) main parts which are the declarative section, the executable section and the exception handling section. The exception handling section is optional and does not have to be included. The syntax follows the format below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IS

[declarative section]

BEGIN

[executable section]

[EXCEPTION]

[exception-handling section]

END;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Variables, custom data types, cursors etc. are declared in the declarative part. The executable part contains the actual piece of code to be executed. It is between the BEGIN and END clauses and must contain at least one RETURN statement. Exceptions are handled in the exception handling block.&lt;/p&gt;

&lt;p&gt;Let's create a function that will return an employee's email in the format &lt;strong&gt;&lt;em&gt;&lt;a href="mailto:firstname.lastname@company.com"&gt;firstname.lastname@company.com&lt;/a&gt;&lt;/em&gt;&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE OR REPLACE FUNCTION get_emp_email(v_id IN NUMBER)
RETURN VARCHAR2
IS 
v_email VARCHAR2(150);
BEGIN
    SELECT LOWER(first_name  || '.' || last_name || '@learnplsql.com')
    INTO v_email
    FROM hr.employees
    WHERE employee_id = v_id;


    RETURN v_email;
END get_emp_email;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the function created above does not have OUT or IN OUT parameters, it can be used in a select statement. Let's get the email of employees with employee ID between 130 and 150.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT EMPLOYEE_ID, FIRST_NAME, LAST_NAME, GET_EMP_EMAIL(EMPLOYEE_ID) EMAIL 
FROM HR.EMPLOYEES
WHERE EMPLOYEE_ID BETWEEN 130 AND 150;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkz5hrw5otd8acfzq039b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkz5hrw5otd8acfzq039b.png" alt="Output table" width="800" height="247"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I find functions convenient for inexpensive joins or operations like the example above. Functions generally make my code neater when used this way. Another use case for functions is to perform computations.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;BONUS&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can practice what you have learnt in Oracle's live SQL platform. The link is &lt;a href="https://livesql.oracle.com/"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>database</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>sql</category>
    </item>
    <item>
      <title>Database Triggers in PostgreSQL: A Deep Dive using AWS RDS</title>
      <dc:creator>Ayokunle Adeniyi</dc:creator>
      <pubDate>Tue, 20 Feb 2024 01:07:04 +0000</pubDate>
      <link>https://forem.com/ayokunle/elevating-database-functionality-with-triggers-a-deep-dive-using-aws-rds-4ieo</link>
      <guid>https://forem.com/ayokunle/elevating-database-functionality-with-triggers-a-deep-dive-using-aws-rds-4ieo</guid>
      <description>&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://unsplash.com/@jrkorpa?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash"&gt;Jr Korpa&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/a-window-with-rain-drops-on-the-glass-E2i7Hftb_rI?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash"&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Database triggers are special types of objects. They look like database procedures and functions but they are only executed in response to a certain event. The procedural code in the body is "triggered" by the specified DML or DDL operations on the database. The permitted operations are &lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt; (DML) and &lt;code&gt;TRUNCATE&lt;/code&gt; (DDL) operations and the execution of the procedural code attached to the trigger could be &lt;code&gt;BEFORE&lt;/code&gt;, &lt;code&gt;AFTER&lt;/code&gt; or &lt;code&gt;INSTEAD OF&lt;/code&gt; the operations, but exactly once &lt;strong&gt;per SQL statement&lt;/strong&gt; or &lt;strong&gt;per modified row&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This blog post uses Postgres and PGSQL as the underlying technology &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Therefore, there are multiple ways to classify triggers. These are &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Based on WHEN you want the trigger function to be executed: These are &lt;code&gt;BEFORE&lt;/code&gt;, &lt;code&gt;AFTER&lt;/code&gt; or &lt;code&gt;INSTEAD OF&lt;/code&gt; triggers.&lt;/li&gt;
&lt;li&gt;Per-row or Per-statement triggers: The keywords that represent per-row triggers are &lt;code&gt;FOR EACH ROW&lt;/code&gt; while the per-statement keyword is &lt;code&gt;FOR EACH STATEMENT&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: &lt;code&gt;FOR EACH STATEMENT&lt;/code&gt; is the default, if it is not declared.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A combination of both classes is required when building the trigger. That is, the trigger must have the WHEN clause (before, after or instead of) and must also either be &lt;em&gt;&lt;strong&gt;per row&lt;/strong&gt;&lt;/em&gt; or &lt;em&gt;&lt;strong&gt;per statement&lt;/strong&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The comprehensive documentation on how to combine both classifications can be found &lt;a href="https://www.postgresql.org/docs/current/sql-createtrigger.html"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Before we dive into the syntax and how to create a trigger in  Postgres, let's consider the use cases for database triggers, its advantages and disadvantages&lt;/p&gt;

&lt;h3&gt;
  
  
  Use cases for database triggers
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Auditing changes on DML events&lt;/li&gt;
&lt;li&gt;Enforcing data and referential integrity that cannot be easily defined using constraints.&lt;/li&gt;
&lt;li&gt;Enhancing security. For instance, preventing DML operations on a table after regular business hours&lt;/li&gt;
&lt;li&gt;Gathering Statistics&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Advantages
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Triggers are efficient when used appropriately. For instance, to carry out an automated action, eliminating the need for manual intervention. &lt;/li&gt;
&lt;li&gt;The functions attached to the trigger can be called in other code or attached to other objects. Therefore, potentially saving development time. A good example would be a trigger function to log all insertions on a table. This sort of function could be table-agnostic and highly reusable.
&lt;/li&gt;
&lt;li&gt;Triggers can offer a high level of control.&lt;/li&gt;
&lt;li&gt;Just like other database objects, triggers can improve overall performance by moving workloads from the application layer to the database layer.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Disadvantages
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Triggers can be complex to write and troubleshoot.&lt;/li&gt;
&lt;li&gt;There is a performance overhead by introducing an additional workload when triggered by the DML operation.&lt;/li&gt;
&lt;li&gt;They are programmatic and easy to alter or disable. Therefore, they can not be fully relied on as security mechanisms and must be used with caution in this case.&lt;/li&gt;
&lt;li&gt;When used as constraints, they are more error-prone because they have to be developed.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Syntax
&lt;/h2&gt;

&lt;p&gt;In Postgres, a trigger is made up of 2 separately defined parts. The function/procedure (basically the block of code that will be executed) and the trigger definition itself. We will get into the syntax shortly using code snippets, but before that, I will describe both the trigger function and the trigger specification.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Trigger Function
&lt;/h3&gt;

&lt;p&gt;The function must be defined before the trigger definition. it does not differ from the typical database function. However, the return type in the function specification must be &lt;code&gt;TRIGGER&lt;/code&gt;. Below is an abridged version of a function specification&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE [ OR REPLACE ] FUNCTION
    name ( [ [ argmode ] [ argname ] argtype [ { DEFAULT | = } default_expr ] [, ...] ] )
    [ RETURNS rettype ]
  { LANGUAGE lang_name
    | sql_body
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where the &lt;code&gt;rettype&lt;/code&gt; is replaced with the keyword &lt;code&gt;TRIGGER&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Trigger Specification
&lt;/h3&gt;

&lt;p&gt;To define the trigger, we use the &lt;code&gt;CREATE TRIGGER&lt;/code&gt; keywords alongside other options. It is also here that the function created earlier is executed.&lt;/p&gt;

&lt;p&gt;Trigger Syntax&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE [ CONSTRAINT ] TRIGGER name { BEFORE | AFTER | INSTEAD OF } { event [ OR ... ] }
    ON table_name
    [ FROM referenced_table_name ]
    [ NOT DEFERRABLE | [ DEFERRABLE ] [ INITIALLY IMMEDIATE | INITIALLY DEFERRED ] ]
    [ FOR [ EACH ] { ROW | STATEMENT } ]
    [ WHEN ( condition ) ]
    EXECUTE PROCEDURE function_name ( arguments )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where the event can be one of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    INSERT
    UPDATE [ OF column_name [, ... ] ]
    DELETE
    TRUNCATE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Example: Audit use case
&lt;/h2&gt;

&lt;p&gt;Let's create a trigger for one of my favourite use cases for triggers, which is auditing operations on a certain table.&lt;br&gt;
Here, we will act as data professionals, setting up a trigger to audit changes to employee details &lt;/p&gt;

&lt;p&gt;First, we create 2 tables, one to house employee details &lt;code&gt;employee&lt;/code&gt; and another to capture relevant audit details &lt;code&gt;employee_audit&lt;/code&gt;. All tables will be under the &lt;code&gt;hr&lt;/code&gt; schema.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- Create demo tables
CREATE table IF NOT EXISTS hr.employees  
( 
    employee_id    INTEGER CONSTRAINT emp_emp_id_pk PRIMARY KEY, 
    first_name     VARCHAR(20),
    last_name      VARCHAR(25)  CONSTRAINT emp_last_name_nn NOT NULL, 
    email          VARCHAR(25)  CONSTRAINT emp_email_nn NOT NULL, CONSTRAINT emp_email_uk UNIQUE (email),  
    phone_number   VARCHAR(20),
    hire_date      DATE  CONSTRAINT emp_hire_date_nn NOT NULL,
    job_id         VARCHAR(10)  CONSTRAINT emp_job_nn NOT NULL,  
    salary         NUMERIC(8,2)  CONSTRAINT emp_salary_min CHECK (salary &amp;gt; 0),
    commission_pct NUMERIC(4,2),
    manager_id     INTEGER CONSTRAINT emp_manager_fk REFERENCES employees(employee_id),
    department_id  INTEGER
);



CREATE table IF NOT EXISTS hr.employees_audit
( 
    employee_id    integer,
    first_name     VARCHAR(20),  
    last_name      VARCHAR(25),  
    email          VARCHAR(25),
    phone_number   VARCHAR(20),  
    hire_date      DATE,
    job_id         VARCHAR(10),
    salary         NUMERIC(8,2),  
    commission_pct NUMERIC(4,2),  
    manager_id     INTEGER,  
    department_id  integer, 
    date_changed   DATE constraint emp_aud_date_change not null,
    client_ip    VARCHAR(25),
    client_host_name VARCHAR(25),
    client_db_username VARCHAR(30),
    client_application varchar(80)
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember! To create a trigger in Postgres, we must create the function that is going to be executed by the trigger. If you need a refresher on writing database functions, please visit the &lt;a href="https://co.hashnode.dev/functions"&gt;functions&lt;/a&gt; post in this series. Here we go!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE OR REPLACE FUNCTION hr.log_employee_changes_function()
  RETURNS TRIGGER 
  LANGUAGE PLPGSQL
  AS
$$
declare
    date_of_change date := current_date;
    client_ip varchar(25);
    hostname varchar(25);
    client_db_user varchar(25);
    client_application varchar(80);
begin
    IF new &amp;lt;&amp;gt; old THEN 

        select pg_catalog.inet_client_addr() 
        into client_ip;

        select client_hostname
        into hostname
        from pg_catalog.pg_stat_activity
        where pid = pg_backend_pid();

        select usename
        into client_db_user
        from pg_catalog.pg_stat_activity
        where pid = pg_backend_pid();

        select application_name
        into client_application
        from pg_catalog.pg_stat_activity
        where pid = pg_backend_pid();

        insert
            into
            hr.employees_audit
        values(
            old.employee_id, 
            old.first_name,
            old.last_name,
            old.email,
            old.phone_number,
            old.hire_date,
            old.job_id,
            old.salary,
            old.commission_pct, 
            old.manager_id,
            old.department_id,
            date_of_change,
            client_ip,
            hostname,
            client_db_user,
            client_application
        ) ;

    END IF;

    RETURN NEW;
END;
$$
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above function returns a &lt;code&gt;TRIGGER&lt;/code&gt;. In its body, it compares the new and old values to see if there is a change. If there is, it collects some additional information specific to that session such as the database user, IP address of the client, as well as the client application name and computes the date of that operation using the &lt;code&gt;CURRENT_DATE&lt;/code&gt; built-in function. &lt;/p&gt;

&lt;p&gt;All these are inserted into the &lt;code&gt;employee_audit&lt;/code&gt; table. But when is it inserted? Is it before or after a change to the employee data? The only way we can find out is when we define our trigger using the &lt;code&gt;CREATE TRIGGER&lt;/code&gt; keywords.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE OR REPLACE TRIGGER log_employee_changes_trigger 
  BEFORE UPDATE
  ON hr.employees
  FOR EACH ROW
  EXECUTE PROCEDURE hr.log_employee_changes_function();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;One thing to notice, the trigger name does not carry the HR schema as a prefix when being defined. This is because the trigger inherits the schema of the table and this is specified in the Postgres doc &lt;a href="https://www.postgresql.org/docs/current/sql-createtrigger.html"&gt;here&lt;/a&gt;. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt;&lt;br&gt;
Triggers are special objects in a database that are executed when certain events happen. There are several cases when a database trigger could be useful such as enforcing constraints, tracking changes on tables that closely relate to business processes, and more. &lt;br&gt;
Although useful, they have their pros and cons. Some advantages are that they provide reusable code, and can be highly efficient, while on the flip side, they cannot be relied on as a security mechanism and may be complex to troubleshoot.&lt;/p&gt;

&lt;p&gt;Overall, we have created a fairly simple database trigger and we can be proud of ourselves for understanding the syntax. Please leave a comment on what you think database triggers can be used for in your organization, project etc.&lt;/p&gt;

</description>
      <category>database</category>
      <category>programming</category>
      <category>postgres</category>
      <category>aws</category>
    </item>
    <item>
      <title>How to write Database Procedures</title>
      <dc:creator>Ayokunle Adeniyi</dc:creator>
      <pubDate>Thu, 15 Feb 2024 13:51:43 +0000</pubDate>
      <link>https://forem.com/ayokunle/database-procedures-3bd7</link>
      <guid>https://forem.com/ayokunle/database-procedures-3bd7</guid>
      <description>&lt;h2&gt;
  
  
  What are Database Procedures
&lt;/h2&gt;

&lt;p&gt;Procedures are commonplace across several different occupations and processes. Simply put, the term 'procedure' is a series of actions conducted in a certain order or manner to achieve a particular result. More popularly, a surgical procedure or an 'Operation' are steps a surgeon would take to carry out a surgical operation on a person. In this series, the focus is on procedures as it relates to database operations. I am assuming we all know what a database is, but for those who don't, you can read it up &lt;a href="https://en.wikipedia.org/wiki/Database"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Procedures also commonly known as stored procedures can be defined, in a simple way, as a reusable block of code that represents a specific business logic stored in a schema. Just a quick example, as an HR manager that is responsible for staff remuneration and payroll; you would have periods where you might want to increase staff salary, change their pay grade, and different other related sub-processes. Achieving this manually (updating) would be very time consuming, cumbersome, and error-prone. With a simple procedure, this can be achieved quite easily at any time the need to perform that particular operation arises.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Let's get right into it. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As we all know, we have different types of databases, but this article would focus on procedures as it relates to Oracle RDBMS. No need to worry, across all databases running on standard SQL, you would only experience little syntactical differences. The examples in this article use the common HR schema and you can practice &lt;a href="https://livesql.oracle.com/"&gt;here&lt;/a&gt;. We are going to start by writing a procedure that takes in an employee ID and returns the employee's name.&lt;/p&gt;

&lt;h2&gt;
  
  
  Syntax
&lt;/h2&gt;

&lt;p&gt;Generally, procedures have 2 parts which are the &lt;strong&gt;specification&lt;/strong&gt; (spec for short) and its &lt;strong&gt;body&lt;/strong&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Procedure Specification
&lt;/h2&gt;

&lt;p&gt;The specification of the procedure serves as the entry point into the procedure. It contains, the procedure name, the parameters related to that procedure (Inputs and Outputs), as well as other optional clauses used to describe the procedure. The specification always starts with the keyword &lt;code&gt;PROCEDURE&lt;/code&gt; and ends with a parameter list which is optional. The parameter list contains the input and output variables for that procedure. Procedures that do not require any parameter list have their specifications written without parenthesis.&lt;/p&gt;

&lt;p&gt;As opposed to functions, procedures can have multiple outputs or none at all. Most times, procedures contain parameters because we need to do similar things but with different data. The procedure with the parameter list sample below has 2 parameters; an input parameter (employee ID with a number datatype) and an output parameter (employee name with a varchar2 datatype). The IN and OUT indicators specify if that parameter is an input into the procedure or an output from the procedure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- procedure specification sample

procedure get_emp_name(v_id in number, v_emp_name out varchar2)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code snippet above shows a procedure specification with a parameter list. Each parameter is separated with a comma while the snippet below shows a procedure specification without a parameter list.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- procedure specification without a parameter list

procedure get_all_emp_names
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;NOTE: General syntax is (parameter_name parameter mode parameter datatype) where the parameter is defined by the developer, the parameter mode is either '&lt;code&gt;IN&lt;/code&gt;' or '&lt;code&gt;OUT&lt;/code&gt;' followed by the corresponding data type of the parameter&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Procedure Body
&lt;/h2&gt;

&lt;p&gt;The procedure body starts after the specification with the keyword '&lt;code&gt;IS&lt;/code&gt;' or '&lt;code&gt;AS&lt;/code&gt;'. The body usually has 3 (three) parts namely:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Declarative part (optional)&lt;/li&gt;
&lt;li&gt;The Executable part&lt;/li&gt;
&lt;li&gt;Exception handling part (optional)
The declarative part: All the variables, data types, and cursors to be used throughout the procedure are declared in this section of the procedure body following the keyword '&lt;code&gt;IS&lt;/code&gt;' or '&lt;code&gt;AS&lt;/code&gt;'.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The executable part contains the program to be executed. This is where the logic of the procedure is written. It is the part that uses all the parameters specified, as well as the variables declared, in other to achieve the purpose of the procedure. Each valid SQL statement must be ended by '&lt;code&gt;;&lt;/code&gt;' identifying the statement and a single statement and syntactically separating it from the following valid SQL statements in the procedure body.&lt;/p&gt;

&lt;p&gt;Exceptions are handled in the exception-handling part of the procedure body. Exception handling is very important in general programming. as this prevents our code from crashing when it runs into an unexpected error. A very simple exception handler code snippet will be used in this article as an example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- declarative part

IS
/* declaring one variable called l_name */ 

l_name VARCHAR2(20);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- executable part (the real logic of the procedure)
begin

    select first_name  || ' ' || last_name 
    into l_name                            -- Variable to hold the result of the query
    from employees
    where employee_id = v_id;   -- Input specified in the specification of the procedure

    v_emp_name := l_name;       -- Output from the procedure.

end get_emp_name;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: The exception handler will be inside the 'BEGIN' and 'END' code block. The exception block code snippet will return 'exception block, employee does not exist' on any exception encountered during the execution of the code between the BEGIN and END block.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BEGIN

    /* insert executable code here */

    -- exception handler
    exception 
        when others then
        v_emp_name := 'exception block, employee does not exist';

END get_emp_name;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Putting it all together
&lt;/h2&gt;

&lt;p&gt;Putting it all together, we have&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE OR REPLACE PROCEDURE get_emp_name(v_id IN NUMBER, v_emp_name OUT VARCHAR2)
IS  
    -- declarative part
    l_name VARCHAR2(20);

    -- executable part
BEGIN
    SELECT first_name  || ' ' || last_name 
    INTO l_name
    FROM employees
    WHERE employee_id = v_id;

    v_emp_name := l_name;

    -- exception handler
    EXCEPTION 
        WHEN OTHERS THEN
    v_emp_name := 'exception block, employee does not exist';

END get_emp_name;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above procedure takes in one parameter (v_id)and returns the employee's full name where the employee ID is the same as the parameter v_id. In the case where the employee ID does not exist, the exception block is triggered and the procedure returns 'exception block, employee does not exist' as the output from the procedure.&lt;/p&gt;

&lt;p&gt;To conclude, procedures are perfect for writing certain business logic that requires multiple statements that must all be executed. A simple and common example is a banking transaction in a banking system where one account is debited and another account is credited and the transaction is recorded in a journal. This operation in SQL, requires 2 update statements and an insert statement before we can say the transaction is successful. If any step is not done, we would want the other executed statements to be rolled back. Procedures are also good for preventing SQL injection and usually have a performance improvement when compared to individually connecting and executing each statement on the database.&lt;/p&gt;

</description>
      <category>database</category>
      <category>sql</category>
      <category>programming</category>
      <category>oracle</category>
    </item>
    <item>
      <title>Mastering Common Database Objects - Series 1 .. n</title>
      <dc:creator>Ayokunle Adeniyi</dc:creator>
      <pubDate>Thu, 15 Feb 2024 13:32:38 +0000</pubDate>
      <link>https://forem.com/ayokunle/common-database-objects-series-1-n-36i3</link>
      <guid>https://forem.com/ayokunle/common-database-objects-series-1-n-36i3</guid>
      <description>&lt;p&gt;In the series, I hope to cover some common database objects such as procedures, functions, triggers etc. while also providing frequent real-life scenarios where these objects are used. The scope of this series will be limited to the oracle PL SQL syntax. However, most objects exist in other database technologies and I am pretty sure that the knowledge gotten here will be transferable to other RDBMS systems.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Get ready!!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Each coming article will focus on one database object only and will contain code snippets. The objects that will be covered are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://dev.to/ayokunle/database-procedures-3bd7"&gt;Procedures&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/ayokunle/functions-in-sql-179h"&gt;Functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://co.hashnode.dev/packages" rel="noopener noreferrer"&gt;Packages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/ayokunle/elevating-database-functionality-with-triggers-a-deep-dive-using-aws-rds-4ieo"&gt;Database Triggers&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;strong&gt;Coming Soon&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Cursors&lt;/li&gt;
&lt;li&gt;Jobs&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>database</category>
      <category>sql</category>
      <category>programming</category>
      <category>awsbigdata</category>
    </item>
    <item>
      <title>Energy Forecasting with LSTM Neural Network</title>
      <dc:creator>Ayokunle Adeniyi</dc:creator>
      <pubDate>Mon, 12 Feb 2024 00:09:00 +0000</pubDate>
      <link>https://forem.com/ayokunle/energy-forecasting-with-lstm-neural-network-2mo1</link>
      <guid>https://forem.com/ayokunle/energy-forecasting-with-lstm-neural-network-2mo1</guid>
      <description>&lt;p&gt;Several methods have been used in energy forecasting over the years. Methods from different disciplines, such as ARMA and ARIMA models from econometrics and probabilistic and regression models from the domain of statistics, which also has an intersection with the symbolic AI field, to name a few. This experiment forecasts energy demand using the Long Short-Term Memory (LSTM) Neural Network models.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Source
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://data.nationalgrideso.com/data-groups/demand"&gt;National Grid Electricity System Operators (National Grid ESO)&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Google Collab Code
&lt;/h2&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;a href="https://colab.research.google.com/drive/1QEZOsjiF1Wls0AePxQmaJoGpFlkX2Fm4?usp=sharing" rel="noopener noreferrer"&gt;
      colab.research.google.com
    &lt;/a&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;Gers, F. A., Schmidhuber, J., &amp;amp; Cummins, F. (2000). Learning to forget: Continual prediction with LSTM. Neural Computation, 12(10), 2451–2471.&lt;/p&gt;

&lt;p&gt;Hochreiter, S., &amp;amp; Schmidhuber, J. (1997). Long Short-Term Memory. Neural Computation, 9(8), 1735–1780. &lt;a href="https://doi.org/10.1162/neco.1997.9.8.1735"&gt;https://doi.org/10.1162/neco.1997.9.8.1735&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yu, Y., Si, X., Hu, C., &amp;amp; Zhang, J. (2019). A Review of Recurrent Neural Networks: LSTM Cells and Network Architectures. Neural Computation, 31(7), 1235–1270. &lt;a href="https://doi.org/10.1162/neco_a_01199"&gt;https://doi.org/10.1162/neco_a_01199&lt;/a&gt;&lt;/p&gt;

</description>
      <category>lstm</category>
      <category>machinelearning</category>
      <category>python</category>
      <category>datascience</category>
    </item>
    <item>
      <title>What to think about when designing, building, managing and operating data systems.</title>
      <dc:creator>Ayokunle Adeniyi</dc:creator>
      <pubDate>Sat, 10 Feb 2024 21:17:07 +0000</pubDate>
      <link>https://forem.com/aws-builders/what-to-think-about-when-designing-building-managing-and-operating-data-systems-19m0</link>
      <guid>https://forem.com/aws-builders/what-to-think-about-when-designing-building-managing-and-operating-data-systems-19m0</guid>
      <description>&lt;p&gt;This post draws extensively from a book I am currently reading and my experience managing data systems (primarily, core data infrastructure). The book is Designing Data-Intensive Applications by Martin Kleppmann.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hey! What is even a Data System?
&lt;/h2&gt;

&lt;p&gt;First, I would like to define a data system in my own words. But before that, I used to think of data systems as databases only, or in a simpler sense, anything that serves as a database. It could be physical sheets of paper, the popular Microsoft Excel, to more complex databases like Oracle, MySQL and MSSQL databases. In essence, I only thought of data systems as systems that store data. As I continue to grow in my career, I have begun to realise that almost all systems are, in some way, data systems. To put out a definition, I would say a data system is any system whether digital or analogue that needs data, processes data and/or stores data. It could be one of these things or a combination of some of the functions aforementioned. I say this because a system that needs that, influences the choices of every other system that is coupled with it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A data system is a system that cares about the shape, size, type, and form of the data it needs to function as designed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fundamental concerns when thinking of designing data systems&lt;br&gt;
There are certain things that one must consider when building efficient systems. These concerns apply to almost all types of digital systems. In this blog, I discuss them in the context of data systems specifically. Anyone designing a data system would have questions such as&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What happens when we have double the traffic that we have now?&lt;/li&gt;
&lt;li&gt;Can we see when something goes wrong, when the fault happens, what caused the fault, who caused it and can we re-create it?&lt;/li&gt;
&lt;li&gt;How do we make that the system can recover from failure either manually or automatically (preferably) without losing or corrupting the data&lt;/li&gt;
&lt;li&gt;Can we make changes to the system in the future?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are building a similar system and have these questions in mind, you are thinking in the right direction. These are typical questions that can be largely grouped into 3 main buckets, which we will delve into shortly. Furthermore, the questions are very broad questions and are far from fully covering all considerations when designing a data system, or even managing an already-built data system.&lt;/p&gt;

&lt;p&gt;Back to the buckets 😅. The main areas of concern are &lt;strong&gt;reliability&lt;/strong&gt;, &lt;strong&gt;scalability&lt;/strong&gt; and &lt;strong&gt;maintainability&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Let's get cracking!!!!!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Reliability
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffsj89feaskcmo2jt6kf6.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffsj89feaskcmo2jt6kf6.jpeg" alt="What do you mean we do not have reliability metrics meme!!!" width="430" height="301"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When referring to people, being reliable is akin to being trustworthy. That is, do people trust you to do what you are tasked and expected to do, even with the numerous distractions life throws at us daily? Likewise, the same is expected from the digital systems we use, much so the mundane everyday systems. We all want our messaging apps to keep our chats no matter what happens to our phones or the servers hosting. Reliability has become an expectation in our society.&lt;/p&gt;

&lt;p&gt;Reliability refers to the correct functioning of a system during its life span according to its design expectations. It also entails the ability of a system to tolerate and recover from &lt;strong&gt;&lt;em&gt;faults&lt;/em&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;em&gt;failures&lt;/em&gt;&lt;/strong&gt;. Other aspects of reliability involve ensuring access to only authorised personnel and keeping the system secure.&lt;/p&gt;

&lt;p&gt;Fault and failures are often used interchangeably; However, they differ from each other, such that, a fault is an issue that results in the deviation from its expected behaviour while a failure occurs when the system fails to provide the required service. It is usually as a result of several faults.&lt;/p&gt;

&lt;p&gt;Typical categories of faults that occur in data systems are&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hardware faults:&lt;/strong&gt; These consist of faults such as Disk crashes, power disruption, cable wear, faulty RAMs e.t.c. You typically hear terms such as mean time between failures (MTBF) and mean time to failure (MTTF). IBM has a good blog post explaining both terms &lt;a href="https://www.ibm.com/blog/mttr-vs-mtbf/#"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Software errors:&lt;/strong&gt; They are also referred to as bugs. Sometimes, software errors could trigger hardware faults. E.g. a bug at the kernel level could cause the hard disk to crash.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Human errors:&lt;/strong&gt; The most common source of errors is human errors. Even with the best intentions, we still tend to make numerous errors. In cybersecurity, it is often said that humans are the weakest links and that shows how error-prone we are although we design and build the most robust systems. Being the most common source of errors, here are some tips on dealing with human errors&lt;/p&gt;

&lt;p&gt;Decouple components and aspects of the system where humans tend to make the most errors. E.g Make configuration modular instead of one big configuration&lt;/p&gt;

&lt;p&gt;Monitor, Monitor, Monitor. Collect telemetry data relevant to the state of a system in parts and as a whole.&lt;/p&gt;

&lt;p&gt;Employ thorough and robust testing. Make use of unit tests and integrated tests to make sure the system acts as intended. Netflix uses an interesting testing method called &lt;a href="https://netflix.github.io/chaosmonkey/"&gt;Chaos Monkey&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Well-designed abstractions can ensure that we interact with systems appropriately by minimizing what we can. Hence, eliminating the room for human errors&lt;/p&gt;

&lt;p&gt;Design to recover quickly. Make error logs understandable, document common faced errors and recovery steps&lt;/p&gt;

&lt;h2&gt;
  
  
  Scalability
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7br4x8hi6o9vq103qgcx.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7br4x8hi6o9vq103qgcx.jpeg" alt="Cosmic imagery from unsplash" width="800" height="760"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Photo by NASA on Unsplash&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Scale can refer to different things in their respective contexts. Concerning data systems, we will focus on one fairly broad concept central to many of the questions commonly asked when scaling vertically (up or down), or scaling horizontally (out or in). That central idea is defining your &lt;strong&gt;load&lt;/strong&gt;, because the scalability of a system can be narrowed down to the question - "How will this system perform if we increase or decrease the load by a factor of X?"&lt;/p&gt;

&lt;p&gt;Essentially load is the amount of computational work done by a system. To define load appropriately, it must be described by some numbers referred to as &lt;em&gt;&lt;u&gt;load parameters&lt;/u&gt;&lt;/em&gt;. These parameters differ per system. It could be in the number of users, requests per second, size of each request, writes vs reads per second, number of cache hits and misses and so on. The idea I am trying to pass here is that defining the load of a system using load parameters will many times differ based on the architecture of the system, and a good way to measure the performance is by using percentiles as opposed to using an average. This is because using percentiles allows you to estimate the number of affected users intuitively.&lt;/p&gt;

&lt;p&gt;Take, for instance, an application, serving 1 million unique users, connecting to a database that has most of its requests processed within 10ms and 1s. Now, that is not a long time in reality but it is critical when chaining different requests to provide a single functionality. If the median response time is 30ms, and the 95th percentile mark is 50ms, we can then easily say that 50,000 (5%) users are experiencing the slowest response times over a given period. Also, the above scenario shows that half of the user base has response times of 30ms and below.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ways to deal with varying load.
&lt;/h3&gt;

&lt;p&gt;When it comes to adapting to load, there is usually a dichotomy of increasing the capacity of your system by increasing the number of machines (scaling horizontally) or by adding resources such as CPU and Memory (scaling vertically). Furthermore, there is a likelihood that both scaling approaches would be used within an entire system as horizontal scaling can become incredibly complex to manage when the members are a lot, especially for stateful systems like data systems.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I like to think of stateful applications as systems where all members must be aware of the current state of the system to act on anything. Therefore, in multi-node architecture where data is across several nodes, all nodes must have a way to know the current state of the entire system.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One key thing to note when considering the multiple approaches is the operability of the system. In choosing your architecture, it is essential to make sure that is it relatively easy to manage, optimize, make changes to and is resilient.&lt;/p&gt;

&lt;h2&gt;
  
  
  Maintainability
&lt;/h2&gt;

&lt;p&gt;This aspect is so important, that there is no point in building a system if it cannot be maintained. Just don't do it. In my experience, I spend a lot of time thinking of how to make any system I work on, easier to maintain. In fact, most of the cost of software is in maintaining it - integrating to new systems, adapting to changes in environment and technology, bug fixes, vulnerability fixes etc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw2s4e4klijitzk6ick41.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw2s4e4klijitzk6ick41.jpeg" alt="Who caused this bug!!! ooops!!!" width="640" height="780"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Maintainability is a big word, so let us define it and deconstruct what it means for a system to be maintainable. &lt;a href="https://www.gasq.org/files/content/gasq/downloads/certification/IQBBA/IQBBA_Standard_glossary_of_terms_used_in_Software_Engineering_1.0.pdf"&gt;IEEE Standard Glossary of Software Engineering Terminology&lt;/a&gt; defines maintainability as: "The ease with which a software system or component can be modified to correct faults, improve performance or other attributes, or adapt to a changed environment."&lt;/p&gt;

&lt;p&gt;Making a system maintainable takes a lot of effort and planning. It is essential to think about maintenance as early as possible when designing a system. There is no one-size-fits-all method to do this, but there are principles that can guide you to achieving this. These design principles for data systems are:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Simplicity&lt;/strong&gt;: this refers to building your system, such that, new engineers and operators find it easy to understand. Remember, today's system is potentially tomorrow's legacy system and we know we like a properly built legacy system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Auditability&lt;/strong&gt;: In my years working on data platforms, the most frequent requests were around who did what on the system and when they did it. Having that fine-grained access control and visibility on what happens can play a vital role in understanding internal user patterns, tracking and reproducing bugs etc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Evolvability&lt;/strong&gt;: No system is likely to stay the same forever, it must evolve to rapidly changing user needs, business needs and other factors such as technological advancement. Systems must be malleable to cater to these changing needs&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Operability&lt;/strong&gt;: Systems must be easy to operate. Good abstractions can make it easy to operate and run smoothly.&lt;/p&gt;

&lt;p&gt;The principles mentioned above may not be exhaustive but will cover most of your maintenance needs if you brainstorm on each carefully. Also, it is important to involve other stakeholders when designing systems.&lt;/p&gt;

&lt;p&gt;If you have reached this part of this article, I must say a &lt;strong&gt;big thank you&lt;/strong&gt; 🎉 for getting to the end. To recap, data systems are more than just databases and analytical systems but extend to other systems that care about the data they receive and give. Because these systems are prominent, they must be designed and built carefully. In so doing, we discussed three (3) main areas to really think about. Those areas are &lt;strong&gt;Reliability, Scalability and Maintainability&lt;/strong&gt;. Reliability is concerned with making the system fault-tolerant, scalability deals with ensuring the system performs optimally even when the load rapidly changes, and maintainability refers to a system being simple and able to evolve.&lt;/p&gt;

&lt;p&gt;Feel free to drop a comment, feedback or question. It will be much appreciated.&lt;/p&gt;

</description>
      <category>systemdesign</category>
      <category>dataengineering</category>
      <category>discuss</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
