Download as pdf or txt
Download as pdf or txt
You are on page 1of 69

1 CoE 135 Object Oriented Programming and Data Structure

Object Oriented
Programming and Data
Structure

CoE 135 Object Oriented Programming and Data Structure

Dr. Dhayaa R. Khudher

pg. 1
2 CoE 135 Object Oriented Programming and Data Structure

List of Contents
1. Introduction to Object Oriented Programming.
1.1. Functional programming vs OOP.
1.2. Object-Oriented Programming (OOP).
1.3. Class.
1.4. Object.
1.5. Encapsulation.
1.6. Abstraction.
1.7. Polymorphism.
1.8. Inheritance.
1.9. Operator Overloading.
1.10. Dynamic Binding.
1.11. Message Passing.
1.12. Interface.
2. Data Structures.
2.1. Introduction.
2.2. Arrays.
2.3. Linked List.
2.4. Stacks.
2.5. Queue.
2.6. Tress.
2.7. Graphs.
2.8. Hash Tables.
2.9. Pointers and References.
2.10. The Strategies for choosing the right data structure.
3. Database Systems.
3.1. Introduction.
3.2. The Database components.
3.3. Database Management System (DBMS).
3.4. The Database architectures.
3.5. Database architecture concept.
3.6. Database data independence.
3.7. Query.
4. Data Modeling.
4.1. Key.
4.2. Foreign key.
4.3. Record.
4.4. Relation.
4.5. Conceptual models.
4.6. Unified Modeling Language (UML).
4.7. Object-Oriented modeling (OOM).
5. Structured Query Language (SQL).
5.1. Data Definition Language (DDL).

pg. 2
3 CoE 135 Object Oriented Programming and Data Structure

5.2. SQL query formulation.


5.3. Update Sub-Language.
5.4. SQL constraints.
5.5. SQL integrity.

pg. 3
4 CoE 135 Object Oriented Programming and Data Structure

The Course Description:


The course gives the fundamental of object-oriented programming, identify data structures useful
to represent specific type of information, and also gives an introduction to database systems and
data modeling, architectures, and fundamental concepts of structured query language.

pg. 4
5 CoE 135 Object Oriented Programming and Data Structure

1. Introduction to Object oriented Programming

1.1 Functional programming vs OOP:

Here are some key differences between procedural and non-procedural programming:
Approach: Procedural programming focuses on the sequence of steps to be followed to
accomplish a task, while non-procedural programming focuses on what needs to be done, without
specifying how it should be done.
Structure: Procedural programming follows a linear structure where the program is divided into
functions or procedures that execute in a specific order, while non-procedural programming uses
a more flexible and modular structure that allows for different ways to solve a problem.
Control flow: In procedural programming, the control flow is determined by the order in which
the functions or procedures are called, while non-procedural programming uses logical rules or
constraints to determine the control flow.
Modularity: Procedural programming emphasizes modularity, which means breaking a program
into smaller, reusable parts, while non-procedural programming allows for the creation of
complex, interconnected modules.
Code readability: Procedural programming may lead to verbose code that is difficult to read and
understand, while non-procedural programming is generally more concise and easier to read and
understand.
Error handling: Procedural programming requires explicit error handling, which means that the
programmer must check for errors and take appropriate actions, while non-procedural
programming often uses automatic error handling mechanisms.
Applications: Procedural programming is often used for tasks that involve repetitive or predictable
tasks, such as data processing or file manipulation, while non-procedural programming is used for
tasks that require complex computations or reasoning, such as artificial intelligence, natural
language processing, or expert systems.

pg. 5
6 CoE 135 Object Oriented Programming and Data Structure

Overall, the choice between procedural and non-procedural programming depends on the specific
requirements of the task at hand, and the programming paradigm that best fits the problem domain.
1.2 Object-oriented programming (OOP):
Object-oriented programming (OOP) is a programming paradigm that emphasizes the use of
objects to represent and manipulate data. C++ is a popular programming language that supports
OOP concepts, making it a great choice for building complex software systems.
In C++, classes are used to define objects, and objects are instances of classes. Classes encapsulate
data and behavior, allowing for modular and reusable code.
Here are some reasons why OOP is a popular choice for software development:
Modularity: OOP allows for modularity, which means that complex programs can be broken
down into smaller, more manageable pieces. Each object in an OOP program represents a modular
component that can be reused in different parts of the program, or in other programs.
Encapsulation: OOP allows for encapsulation, which means that an object's data and behavior are
kept private and can only be accessed through well-defined interfaces. Encapsulation helps to
ensure that the program is more secure and less prone to errors.
Abstraction: OOP allows for abstraction, which means that objects can be viewed at different
levels of detail, depending on the needs of the programmer. Abstraction helps to simplify the
design and implementation of the program by focusing on the essential features of the objects.
Inheritance: OOP allows for inheritance, which means that new classes can be derived from
existing classes, inheriting their properties and methods. Inheritance helps to reduce code
duplication and promotes code reuse.
Polymorphism: OOP allows for polymorphism, which means that objects can be treated as
instances of their own class, or as instances of a more general class. Polymorphism helps to
simplify the program by allowing for the use of generic methods and classes that can be applied to
different types of objects.
Overall, OOP provides a powerful and flexible way to design and implement complex software
systems, allowing for modularity, encapsulation, abstraction, inheritance, and polymorphism.
These features help to make programs more modular, maintainable, and extensible, while also
reducing the likelihood of errors and bugs.
Some popular programming languages for OOP include:
Java: Java is one of the most popular languages for OOP due to its built-in support for OOP
concepts such as classes, objects, inheritance, and polymorphism. It also has a large standard
library and many third-party libraries and frameworks that make it easy to build complex OOP
applications.

pg. 6
7 CoE 135 Object Oriented Programming and Data Structure

C++: C++ is a popular language for OOP due to its performance and support for low-level memory
manipulation. It provides support for classes, objects, inheritance, and polymorphism, and allows
for fine-grained control over memory management.
Python: Python has a simple and intuitive syntax that makes it easy to implement OOP concepts.
It also provides support for classes, objects, inheritance, and polymorphism, as well as many
libraries and frameworks that make it easy to build complex OOP applications.
C#: C# is a popular language for OOP due to its built-in support for classes, objects, inheritance,
and polymorphism, as well as its integration with the .NET Framework and support for Windows
application development.
Ultimately, the choice of language for OOP will depend on the specific requirements of the project,
including factors such as performance, ease of use, availability of libraries and frameworks, and
the developer's familiarity with the language.
1.3 Class
In C++, a class is a user-defined data type that encapsulates data and the methods (functions) that
operate on that data. It provides a blueprint for creating objects (instances of the class) that have
their own set of data and methods.

Here is an example of a simple C++ class:


class Person {
public:
// Constructor
Person(std::string name, int age) {
this->name = name;

pg. 7
8 CoE 135 Object Oriented Programming and Data Structure

this->age = age;
}

// Method to get the person's name


std::string getName() {
return name;
}

// Method to get the person's age


int getAge() {
return age;
}

private:
std::string name;
int age;
};

In this example, the Person class has two data members (name and age) and two methods (getName
and getAge). The public keyword is used to specify that these methods are accessible from outside
the class. The private keyword is used to specify that the data members are only accessible from
within the class.
The constructor is a special method that is called when an object of the class is created. It initializes
the data members of the object with the values passed in as arguments.
To create an object of the Person class, you can use the following code:
Person person("John", 30);
std::cout << "Name: " << person.getName() << std::endl;
std::cout << "Age: " << person.getAge() << std::endl;

This will create a Person object with the name "John" and age 30, and then print out the name
and age using the getName and getAge methods.

1.4 Object
In C++, an object is an instance of a class. It is a variable that contains data and the methods
(functions) that operate on that data, as defined by the class.
When you create an object, the constructor of the class is called to initialize the object's data
members. You can then use the object to call its methods and access its data members.

pg. 8
9 CoE 135 Object Oriented Programming and Data Structure

Here's an example of creating an object of a class called Car:


class Car {
public:
// Constructor
Car(std::string make, std::string model, int year) {
this->make = make;
this->model = model;
this->year = year;
}

// Method to get the car's make


std::string getMake() {
return make;
}

// Method to get the car's model


std::string getModel() {
return model;
}

// Method to get the car's year


int getYear() {
return year;
}

private:
std::string make;
std::string model;

pg. 9
10 CoE 135 Object Oriented Programming and Data Structure

int year;
};

// Create an object of the Car class


Car myCar("Toyota", "Camry", 2022);

// Call methods on the object


std::cout << "Make: " << myCar.getMake() << std::endl;
std::cout << "Model: " << myCar.getModel() << std::endl;
std::cout << "Year: " << myCar.getYear() << std::endl;

In this example, the Car class has three data members (make, model, and year) and three methods
(getMake, getModel, and getYear). The myCar object is created using the constructor of the class
with the arguments "Toyota", "Camry", and 2022. The methods of the object are then called to
retrieve its data members and print them to the console.
In summary, an object in C++ is an instance of a class that contains data and methods defined by
the class. It is created using the constructor of the class and can be used to call its methods and
access its data members.

1.5 Encapsulation
Encapsulation is one of the fundamental concepts of object-oriented programming (OOP) and is
supported by C++. It refers to the practice of bundling data and behavior within a single entity,
i.e., a class, and hiding the implementation details from the outside world.
In C++, encapsulation is achieved using access specifiers. There are three access specifiers in C++:
public, private, and protected.
public members are accessible from anywhere in the program, including outside the class. They
are used to provide an interface to the class that can be accessed by other parts of the program.
private members are only accessible from within the class. They are used to hide implementation
details and prevent direct access to the class's data members from outside the class.
protected members are similar to private members, but they can be accessed by derived classes.

pg. 10
11 CoE 135 Object Oriented Programming and Data Structure

Here's an example of a class that uses encapsulation to hide its data members:
class BankAccount {
public:
BankAccount(std::string owner, double balance) {
this->owner = owner;
this->balance = balance;
}

void deposit(double amount) {


balance += amount;
}

void withdraw(double amount) {


balance -= amount;
}

double getBalance() {
return balance;
}

private:
std::string owner;
double balance;
};

pg. 11
12 CoE 135 Object Oriented Programming and Data Structure

In this example, the owner and balance data members are declared as private. This means that they
can only be accessed from within the class, and not from outside. The deposit, withdraw, and
getBalance methods are declared as public, which means they can be accessed from outside the
class. These methods provide an interface to the class that can be used to interact with its data
members indirectly.
Using encapsulation in this way can provide several benefits. It can improve code maintainability,
reduce the risk of bugs caused by direct manipulation of data members, and prevent unauthorized
access to sensitive data.
1.6 Abstraction
Abstraction is another important concept in object-oriented programming (OOP) that is supported
by C++. It refers to the practice of simplifying complex systems by only showing the essential
features to the user and hiding the implementation details. Abstraction is typically achieved using
abstract classes and pure virtual functions.
In C++, an abstract class is a class that has at least one pure virtual function. A pure virtual function
is a function that is declared in the class but has no implementation (using). The purpose of a pure
virtual function is to provide an interface that derived classes must implement (use). An abstract
class cannot be instantiated, but it can be used as a base class for derived classes.

pg. 12
13 CoE 135 Object Oriented Programming and Data Structure

Here's an example of an abstract class that uses abstraction to provide an interface to its derived
classes:
class Shape {
public:
virtual double area() = 0;
virtual double perimeter() = 0;
};

In this example, the Shape class is an abstract class that has two pure virtual functions: area() and
perimeter(). These functions are used to define the essential features of a shape, but their
implementation is left up to the derived classes. Any class that derives from the Shape class must
provide implementations for these functions, otherwise it will also be considered an abstract class
and cannot be instantiated.
Here's an example of a derived class that implements the Shape class:
class Circle : public Shape {
public:
Circle(double radius) {
this->radius = radius;
}

double area() {
return 3.14 * radius * radius;
}

double perimeter() {
return 2 * 3.14 * radius;
}

private:
double radius;
};

In this example, the Circle class derives from the Shape class and provides implementations for
the area() and perimeter() functions. These functions define the essential features of a circle and
provide a concrete implementation for the abstract Shape class.
Using abstraction in this way can provide several benefits. It can simplify complex systems by
providing a high-level interface that is easy to use, and it can enable code reuse by providing a
common interface that can be implemented by multiple classes.

pg. 13
14 CoE 135 Object Oriented Programming and Data Structure

1.7 Polymorphism
Polymorphism is another important concept in object-oriented programming (OOP) that is
supported by C++. It refers to the ability of objects to take on multiple forms, or to have multiple
behaviors, depending on the context in which they are used. Polymorphism can be achieved in
C++ using virtual functions and function overloading.
In C++, a virtual function is a function that is declared in the base class and overridden in the
derived class. When a virtual function is called on a base class pointer or reference that points to
an object of the derived class, the derived class's implementation of the function is called instead
of the base class's implementation. This allows objects of the same base class type to have different
behaviors, depending on their derived class type.

Here's an example of a base class that defines a virtual function:


class Shape {
public:
virtual void draw() {
std::cout << "Drawing a shape\n";
}
};

In this example, the Shape class has a virtual function called draw(). This function is overridden
in the derived classes to provide different implementations for different types of shapes.

pg. 14
15 CoE 135 Object Oriented Programming and Data Structure

Here's an example of a derived class that overrides the draw() function:


class Circle : public Shape {
public:
void draw() {
std::cout << "Drawing a circle\n";
}
};

In this example, the Circle class derives from the Shape class and provides its own implementation
of the draw() function. When draw() is called on a Circle object through a base class pointer or
reference, the Circle implementation of the function is called.
Another way to achieve polymorphism in C++ is through function overloading. Function
overloading allows multiple functions with the same name to coexist in the same scope, as long as
they have different parameter lists. When a function is called, the compiler determines which
version of the function to call based on the types and number of arguments provided.
Here's an example of function overloading:
void print(int num) {
std::cout << "Printing an integer: " << num << "\n";
}

void print(double num) {


std::cout << "Printing a double: " << num << "\n";
}

In this example, there are two functions called print(). One takes an int parameter and one takes
a double parameter. When print() is called with an int argument, the first version of the function
is called. When it's called with a double argument, the second version of the function is called.
Using polymorphism in this way can provide several benefits. It can enable code reuse by allowing
objects to be used in multiple contexts, and it can simplify code by providing a common interface
for objects with different behaviors.

pg. 15
16 CoE 135 Object Oriented Programming and Data Structure

1.8 Inheritance
Inheritance is a feature in C++ that allows a class to derive properties and behaviors from a base
class. The derived class can inherit the data members and member functions of the base class and
can also add its own unique features. The base class is also known as the parent class or super
class, while the derived class is known as the child class or sub class.
Here's an example of inheritance in C++:
class Shape {
public:
void setWidth(int w) { width = w; }
void setHeight(int h) { height = h; }
protected:
int width, height;
};

class Rectangle : public Shape {


public:
int getArea() { return width * height; }
};

int main() {
Rectangle rect;
rect.setWidth(5);
rect.setHeight(7);
std::cout << "Area of Rectangle: " << rect.getArea() << std::endl;
return 0;
}

In this example, we define a base class Shape that has two data members width and height and
two member functions setWidth() and setHeight() to set the values of these data members. We
also define a derived class Rectangle that inherits from Shape using the public access specifier.
The Rectangle class has a member function getArea() that calculates and returns the area of the
rectangle using the width and height data members inherited from the Shape class.
In the main() function, we create an object of the Rectangle class and set its width and height
using the setWidth() and setHeight() functions inherited from the Shape class. We then call the
getArea() function to calculate and print the area of the rectangle.
Note that in this example, the width and height data members are declared as protected in the Shape
class. This means that they can be accessed by the Rectangle class and any other derived classes,
but not by code outside of the class hierarchy. If the data members were declared as private, they
would not be accessible to the Rectangle class or any other derived classes, and would need to be
accessed through member functions like setWidth() and setHeight(). If the data members were
declared as public, they would be accessible to any code that has access to the Rectangle object.

pg. 16
17 CoE 135 Object Oriented Programming and Data Structure

1.9 Operator overloading


Operator overloading is a feature in C++ that allows you to define the behavior of an operator
when it is applied to user-defined types. In other words, you can redefine the behavior of built-in
operators such as +, -, *, /, and = for your own classes.
To overload an operator, you need to define a function that has the operator symbol as its name,
and use the operator keyword followed by the symbol. The function should take one or more
arguments of the type(s) you want to overload the operator for, and return a value of the appropriate
type. For example, to overload the + operator for a custom class MyClass, you would define a
function like this:
class MyClass {
public:
int value;

MyClass() {
value = 0;
}

MyClass(int v) {
value = v;
}

MyClass operator+(const MyClass& other) {


return MyClass(value + other.value);
}
};

int main() {
MyClass a(5);
MyClass b(7);
MyClass c = a + b;
std::cout << c.value << std::endl; // Output: 12
return 0;
}

In this example, we define a class MyClass with a single data member value, and two constructors:
a default constructor that sets value to 0, and a constructor that takes an integer argument and sets
value to that argument. We then overload the + operator using the operator+ function. This function
takes a reference to another MyClass object as its argument, adds the value of the other object to
the value of the current object, and returns a new MyClass object with the sum as its value.

pg. 17
18 CoE 135 Object Oriented Programming and Data Structure

In the main() function, we create two MyClass objects a and b with values 5 and 7, respectively.
We then add them together using the + operator, which calls the overloaded operator+ function,
and store the result in a new MyClass object c. Finally, we print the value of c.
Note that you can overload many other operators in addition to +, such as -, *, /, %, ==, !=, <, >,
<=, >=, ++, --, and many more. Operator overloading can be a powerful tool for creating more
expressive and intuitive code, but it should be used judiciously to avoid confusion and maintain
readability.
1.10 Dynamic binding
Dynamic binding is a feature of C++ that allows the selection of the function to be called at
runtime, based on the type of the object that the function is called on. This is also known as late
binding or runtime polymorphism.
Dynamic binding is achieved in C++ using virtual functions. When a virtual function is called on
a base class pointer or reference, the actual function that is called is determined by the type of
the object that the pointer or reference points to at runtime.
Here's an example that demonstrates dynamic binding:
class Animal {
public:
virtual void makeSound() {
std::cout << "The animal makes a sound\n";
}
};

class Dog : public Animal {


public:
void makeSound() {
std::cout << "The dog barks\n";
}
};

class Cat : public Animal {


public:
void makeSound() {
std::cout << "The cat meows\n";
}
};

int main() {
Animal* animal1 = new Dog();
Animal* animal2 = new Cat();

animal1->makeSound(); // Output: The dog barks

pg. 18
19 CoE 135 Object Oriented Programming and Data Structure

animal2->makeSound(); // Output: The cat meows

delete animal1;
delete animal2;

return 0;
}

1.11 Message passing


Message passing is a programming paradigm that is commonly used in C++ to enable
communication and coordination between different objects or components of a program. It
involves passing messages, which can contain data or instructions, between different objects or
components of a program to coordinate their behavior.
In C++, message passing can be implemented using several different mechanisms, including
function calls, function pointers, events, and message queues.
One common way to implement message passing in C++ is through function calls. In this
approach, an object sends a message to another object by calling one of its functions, passing any
necessary parameters as arguments. The receiving object then processes the message and may send
a message back to the original object if necessary.
Here's an example of message passing using function calls:
class Sender {
public:
void sendMessage(std::string message, Receiver* receiver) {
receiver->receiveMessage(message);
}
};

class Receiver {
public:
void receiveMessage(std::string message) {
std::cout << "Received message: " << message << "\n";
}
};

int main() {
Sender sender;
Receiver receiver;

sender.sendMessage("Hello, world!", &receiver);

return 0;
}

pg. 19
20 CoE 135 Object Oriented Programming and Data Structure

In this example, there are two classes: Sender and Receiver. The Sender class has a function called
sendMessage() that takes a message and a pointer to a Receiver object as parameters. The Receiver
class has a function called receiveMessage() that takes a message as a parameter and processes it.
In the main() function, a Sender object is created and a Receiver object is created. The
sendMessage() function of the Sender object is then called, passing a message and a pointer to the
Receiver object as parameters. The receiveMessage() function of the Receiver object is then called,
and it processes the message.
Another way to implement message passing in C++ is through function pointers. In this approach,
an object can store a pointer to a function as a member variable, and other objects can send
messages to it by calling that function through the pointer.
Here's an example of message passing using function pointers:
class Receiver {
public:
typedef void (Receiver::*MessageHandler)(std::string);

void handleMessage(std::string message) {


if (m_handler != nullptr) {
(this->*m_handler)(message);
}
}

void setMessageHandler(MessageHandler handler) {


m_handler = handler;
}

private:
MessageHandler m_handler = nullptr;
};

class Sender {
public:
void sendMessage(std::string message, Receiver* receiver,
Receiver::MessageHandler handler) {
receiver->setMessageHandler(handler);
receiver->handleMessage(message);
}
};

class Logger {
public:
void log(std::string message) {
std::cout << "Logging message: " << message << "\n";

pg. 20
21 CoE 135 Object Oriented Programming and Data Structure

}
};

int main() {
Receiver receiver;
Sender sender;
Logger logger;

sender.sendMessage("Hello, world!", &receiver, &Logger::log);

return 0;
}
In this example, the Receiver class has a member variable called m_handler, which is a pointer to
a function that takes a string parameter. The Receiver class also has a function called
handleMessage() that calls the function pointed to by m_handler and passes it the message as a
parameter. The setMessageHandler() function sets the value of m_handler.

The Sender class has a function called sendMessage() that takes a message, a pointer to a Receiver
object, and a pointer to a function as parameters. It sets the message handler of the Receiver object
to
1.12 Interface
In C++, an interface is a class that defines a set of pure virtual functions. A pure virtual function
is a virtual function that has no implementation in the base class and must be overridden by any
class that inherits from it.
An interface is a way to specify a contract or a set of capabilities that a class must implement in
order to be considered compatible with that interface. It is a way to achieve polymorphism and to
decouple the interface from the implementation.
Here's an example of an interface in C++:
class Shape {
public:
virtual double area() const = 0;
virtual double perimeter() const = 0;
};

class Circle : public Shape {


public:
Circle(double r) : radius(r) {}
double area() const override { return 3.14 * radius * radius; }
double perimeter() const override { return 2 * 3.14 * radius; }
private:

pg. 21
22 CoE 135 Object Oriented Programming and Data Structure

double radius;
};

class Rectangle : public Shape {


public:
Rectangle(double w, double h) : width(w), height(h) {}
double area() const override { return width * height; }
double perimeter() const override { return 2 * (width + height); }
private:
double width, height;
};

int main() {
Shape* shapes[] = { new Circle(5.0), new Rectangle(3.0, 4.0) };
for (auto shape : shapes) {
std::cout << "Area: " << shape->area() << ", Perimeter: " << shape-
>perimeter() << std::endl;
}
return 0;
}
In this example, we define an interface Shape that has two pure virtual functions: area() and
perimeter(). We also define two classes Circle and Rectangle that inherit from Shape and
implement the two functions.
In the main() function, we create an array of Shape pointers and initialize it with instances of Circle
and Rectangle. We then use a for loop to iterate through the array and call the area() and perimeter()
functions on each shape.
Since Circle and Rectangle inherit from Shape and implement its virtual functions, they are
considered compatible with the Shape interface and can be used interchangeably wherever a Shape
object is expected. This is an example of polymorphism achieved through interfaces.

pg. 22
23 CoE 135 Object Oriented Programming and Data Structure

2. Data Structures:
2.1. Introduction
There is no one "best" language for data structures as different languages can be better suited for
different use cases and scenarios. However, there are several languages that are commonly used
for implementing data structures:
C++: C++ is a popular language for implementing data structures due to its ability to create low-
level memory manipulations, which is particularly useful when working with pointers, arrays, and
other data structures.
Java: Java provides a rich set of built-in data structures such as arrays, lists, maps, and sets, making
it easy to implement and work with data structures. Java also has a garbage collector that
automatically manages memory, making it easier to avoid common memory-related bugs.
Python: Python has a simple and intuitive syntax that makes it easy to implement and experiment
with data structures. Python also provides several built-in data structures such as lists, tuples,
dictionaries, and sets, as well as several libraries for working with more complex data structures
such as trees and graphs.
C#: C# provides a rich set of built-in data structures such as arrays, lists, queues, and stacks, as
well as support for object-oriented programming principles, making it easier to create complex
and scalable data structures.
Ultimately, the choice of language will depend on the specific use case and requirements of the
project. It is important to consider factors such as performance, ease of use, scalability, and the
availability of libraries and frameworks when selecting a language for implementing data
structures.

pg. 23
24 CoE 135 Object Oriented Programming and Data Structure

C++ is a programming language that supports various data structures to manage and store data
efficiently. Some commonly used data structures in C++ are:
Arrays: Arrays are a collection of elements of the same data type, which are stored in contiguous
memory locations. The size of an array is fixed and defined at compile time.
Linked List: A linked list is a collection of nodes that are linked together using pointers. Each node
contains a data element and a pointer to the next node in the list. Linked lists can be used to
implement dynamic data structures like stacks, queues, and hash tables.
Stack: A stack is a data structure that follows the LIFO (Last In First Out) principle. Elements are
added and removed from the top of the stack. Stacks can be implemented using arrays or linked
lists.
Queue: A queue is a data structure that follows the FIFO (First In First Out) principle. Elements
are added at the rear and removed from the front of the queue. Queues can be implemented using
arrays or linked lists.
Tree: A tree is a data structure that consists of nodes connected by edges. Each node can have zero
or more child nodes. Trees are used to represent hierarchical data like file systems, organization
charts, and family trees.
Graph: A graph is a data structure that consists of vertices and edges. Vertices represent objects or
entities, and edges represent the connections between them. Graphs can be used to represent
complex relationships between data.
Hash Table: A hash table is a data structure that stores data in key-value pairs. Hash functions are
used to map the keys to unique indices in an array. Hash tables are used to implement dictionaries
and other associative data structures.
These data structures are essential building blocks for various algorithms and applications in
computer science and software development. C++ provides a rich set of libraries and frameworks
for working with these data structures efficiently.
2.2. Arrays:
In C++, an array is a collection of elements of the same data type, stored in contiguous memory
locations. Each element of an array can be accessed using an index or a subscript, which starts
from zero.

pg. 24
25 CoE 135 Object Oriented Programming and Data Structure

To declare an array in C++, you need to specify its data type and its size. For example, to declare
an array of integers with ten elements, you can use the following syntax:
int myArray[10];

You can initialize an array at the time of declaration using curly braces. For example, to initialize
an array of integers with values 1, 2, 3, 4, 5, you can use the following syntax:
int myArray[] = {1, 2, 3, 4, 5};

You can loop through an array using a for loop. For example, to print all the elements of an array,
you can use the following syntax:
for (int i = 0; i < 5; i++) {
cout << myArray[i] << " ";
}

Here, the loop runs from 0 to 4 because the array has 5 elements.
Arrays in C++ are a powerful data structure that can be used to store and manipulate large amounts
of data efficiently. However, it is important to make sure that you do not exceed the bounds of the
array while accessing its elements, as this can lead to runtime errors.
2.3. Linked List
In C++, a linked list is a data structure consisting of a sequence of nodes, where each node contains
a data element and a pointer to the next node in the sequence. The last node in the sequence points
to a null pointer, indicating the end of the list. Linked lists can be used to implement dynamic data
structures such as stacks, queues, and hash tables.

To declare a linked list, you first need to declare a structure for the nodes. The structure contains
two members: a data element and a pointer to the next node in the list. For example, to declare a
structure for an integer node, you can use the following syntax:
struct Node {
int data;
Node* next;
};

pg. 25
26 CoE 135 Object Oriented Programming and Data Structure

To create a linked list, you first need to declare a pointer to the head of the list, which points to the
first node in the list. Initially, the head points to a null pointer, indicating an empty list. For
example, to declare a pointer to the head of the list, you can use the following syntax:
Node* head = nullptr;

To insert an element into a linked list, you first need to create a new node and assign the data
element to it. Then, you need to link the new node to the list by updating the next pointer of the
previous node and the new node. For example, to insert a new integer element into the list, you
can use the following syntax:
Node* newNode = new Node;
newNode->data = 10;
newNode->next = head;
head = newNode;

This code creates a new node with a data element of 10 and links it to the head of the list.
To traverse a linked list, you can use a while loop to iterate through the nodes in the list, starting
from the head. For example, to print all the elements in the list, you can use the following syntax:
Node* current = head;
while (current != nullptr) {
cout << current->data << " ";
current = current->next;
}

This code starts from the head of the list and prints the data element of each node until it reaches
the end of the list.
Linked lists in C++ are a powerful data structure that can be used to implement dynamic data
structures efficiently. However, it is important to properly manage memory allocation and
deallocation to avoid memory leaks and other errors.
2.4. Stacks:
In C++, a stack is a data structure that follows the Last-In-First-Out (LIFO) principle, where the
last element added to the stack is the first one to be removed. A stack can be implemented using
an array or a linked list.

pg. 26
27 CoE 135 Object Oriented Programming and Data Structure

To declare a stack in C++, you can use the stack template class from the STL (Standard Template
Library). For example, to declare a stack of integers, you can use the following syntax:
#include <stack>
using namespace std;

stack<int> myStack;

To push an element onto a stack, you can use the push() member function of the stack class. For
example, to push an integer value of 10 onto the stack, you can use the following syntax:
myStack.push(10);

To remove the top element from a stack, you can use the pop() member function of the stack class.
For example, to remove the top element from the stack, you can use the following syntax:
myStack.pop();

To access the top element of a stack, you can use the top() member function of the stack class. For
example, to access the top element of the stack, you can use the following syntax:
int topElement = myStack.top();

To check if a stack is empty, you can use the empty() member function of the stack class. For
example, to check if the stack is empty, you can use the following syntax:
if (myStack.empty()) {
cout << "The stack is empty." << endl;
}

pg. 27
28 CoE 135 Object Oriented Programming and Data Structure

Stacks in C++ are a useful data structure that can be used to implement algorithms such as depth-
first search and backtracking. They can also be used to reverse the order of elements in a sequence
or to evaluate postfix expressions.
2.5. Queue
In C++, a queue is a data structure that follows the First-In-First-Out (FIFO) principle, where the
first element added to the queue is the first one to be removed. A queue can be implemented using
an array or a linked list.

To declare a queue in C++, you can use the queue template class from the STL (Standard Template
Library). For example, to declare a queue of integers, you can use the following syntax:
#include <queue>
using namespace std;

queue<int> myQueue;

To enqueue an element into a queue, you can use the push() member function of the queue class.
For example, to enqueue an integer value of 10 into the queue, you can use the following syntax:
myQueue.push(10);

To dequeue an element from a queue, you can use the pop() member function of the queue class.
For example, to dequeue the first element from the queue, you can use the following syntax:
myQueue.pop();

pg. 28
29 CoE 135 Object Oriented Programming and Data Structure

To access the front element of a queue, you can use the front() member function of the queue class.
For example, to access the front element of the queue, you can use the following syntax:
int frontElement = myQueue.front();

To access the back element of a queue, you can use the back() member function of the queue class.
For example, to access the back element of the queue, you can use the following syntax:
int backElement = myQueue.back();

To check if a queue is empty, you can use the empty() member function of the queue class. For
example, to check if the queue is empty, you can use the following syntax:
if (myQueue.empty()) {
cout << "The queue is empty." << endl;
}

Queues in C++ are a useful data structure that can be used to implement algorithms such as
breadth-first search and simulations involving waiting lines. They can also be used to sort elements
in a sequence or to implement a buffer in a communication system.
2.6. Tree:
In C++, a tree is a hierarchical data structure that consists of nodes connected by edges. The
topmost node of a tree is called the root node, and each node in the tree can have zero or more
child nodes. Trees are commonly used to represent hierarchical structures such as file systems,
organization charts, and decision trees.

pg. 29
30 CoE 135 Object Oriented Programming and Data Structure

There are many types of trees, including binary trees, binary search trees, AVL trees, red-black
trees, and many more. In this explanation, we will discuss the implementation of a binary tree in
C++.
To declare a binary tree in C++, we can define a tree node struct with a left and right child pointer,
and a data value. For example:
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
To create a binary tree, we can recursively create left and right subtrees for each node. For example,
to create a binary tree with root node value of 1, left child value of 2, and right child value of 3,
we can use the following code:
TreeNode* root = new TreeNode(1);
root->left = new TreeNode(2);
root->right = new TreeNode(3);

There are three common ways to traverse a binary tree: inorder, preorder, and postorder. In inorder
traversal, we first traverse the left subtree, then the root node, and then the right subtree. In preorder
traversal, we first visit the root node, then the left subtree, and then the right subtree. In postorder
traversal, we first visit the left subtree, then the right subtree, and then the root node.
To traverse a binary tree, we can use recursion. For example, to perform an inorder traversal of a
binary tree, we can use the following code:
void inorderTraversal(TreeNode* root) {
if (root == nullptr) {
return;
}
inorderTraversal(root->left);
cout << root->val << " ";
inorderTraversal(root->right);
}

To insert a node into a binary tree, we can recursively traverse the tree to find the appropriate
position to insert the new node. For example, to insert a new node with value 4 into a binary tree,
we can use the following code:
void insertNode(TreeNode* root, int val) {
if (root == nullptr) {
root = new TreeNode(val);
return;
}

pg. 30
31 CoE 135 Object Oriented Programming and Data Structure

if (val < root->val) {


insertNode(root->left, val);
} else {
insertNode(root->right, val);
}
}

To delete a node from a binary tree, we must first find the node to be deleted and then restructure
the tree to maintain the binary search tree property. This can be a complex operation, and there are
many ways to perform deletion depending on the requirements of the application. One common
way is to replace the node to be deleted with its inorder predecessor or successor, and then delete
the predecessor or successor. Another way is to promote the left or right subtree to replace the
deleted node.
Binary trees in C++ are a versatile data structure that can be used to solve many types of problems,
including searching, sorting, and pathfinding. They can also be used to implement decision trees
and artificial intelligence algorithms.
2.7. Graphs:
In C++, a graph is a data structure that consists of a set of vertices (also called nodes or points)
and a set of edges that connect these vertices. Graphs are widely used in computer science,
engineering, and many other fields to model complex systems and relationships between data.

There are two main types of graphs: directed graphs and undirected graphs. In a directed graph,
each edge has a direction, while in an undirected graph, edges have no direction.
Graphs can be represented in various ways in C++. One common representation is using an
adjacency matrix or an adjacency list. An adjacency matrix is a two-dimensional array where the
entry (i, j) is 1 if there is an edge from vertex i to vertex j, and 0 otherwise. An adjacency list is a
list of lists where each vertex has a list of its neighboring vertices.
Here's an example of how to declare a graph using an adjacency list in C++:
#include <iostream>
#include <vector>
using namespace std;

class Graph {
private:

pg. 31
32 CoE 135 Object Oriented Programming and Data Structure

int V; // number of vertices


vector<int>* adj; // adjacency list

public:
Graph(int V) {
this->V = V;
adj = new vector<int>[V];
}

void addEdge(int v, int w) {


adj[v].push_back(w);
}

void printGraph() {
for (int i = 0; i < V; i++) {
cout << "Vertex " << i << " is connected to: ";
for (auto j : adj[i]) {
cout << j << " ";
}
cout << endl;
}
}
};

In this example, we declare a class Graph with a private member V to store the number of vertices,
and a private member adj to store the adjacency list. The constructor initializes adj as an array of
vectors with V elements.
The method addEdge adds an edge between two vertices v and w by pushing w to the vector at
index v in adj.
The method printGraph prints the adjacency list for each vertex in the graph.
To create and use this graph, we can use the following code:
int main() {
Graph g(4);
g.addEdge(0, 1);
g.addEdge(0, 2);
g.addEdge(1, 2);
g.addEdge(2, 0);
g.addEdge(2, 3);
g.addEdge(3, 3);

g.printGraph();

pg. 32
33 CoE 135 Object Oriented Programming and Data Structure

return 0;
}

This code creates a graph with 4 vertices and adds edges between them. The printGraph method is
then called to print the adjacency list for each vertex.
Graphs are a fundamental data structure in computer science and are used in many applications,
including social networks, computer networks, and transportation systems. A wide variety of
algorithms exist for working with graphs, such as traversal algorithms (e.g., BFS and DFS),
shortest path algorithms (e.g., Dijkstra's algorithm and Bellman-Ford algorithm), and minimum
spanning tree algorithms (e.g., Prim's algorithm and Kruskal's algorithm).

2.8. Hash Tables:


Hash tables in C++ are a type of data structure that allow for efficient insertion, deletion, and
retrieval of data by mapping keys to their corresponding values using a hash function. A hash
function takes a key and returns a unique index in the hash table where the value associated with
that key is stored.
The basic idea behind a hash table is to use an array of fixed size and map keys to array indices
using a hash function. In C++, hash tables are implemented using the unordered_map class
provided by the standard library.

pg. 33
34 CoE 135 Object Oriented Programming and Data Structure

Here's an example of how to use unordered_map in C++ to implement a hash table:


#include <iostream>
#include <unordered_map>
using namespace std;

int main() {
unordered_map<string, int> hash_table; // declare a hash table with string
keys and integer values

// insert elements into the hash table


hash_table["apple"] = 1;
hash_table["banana"] = 2;
hash_table["cherry"] = 3;

// access elements in the hash table


cout << "The value of apple is: " << hash_table["apple"] << endl;

// iterate over the elements in the hash table


for (auto it = hash_table.begin(); it != hash_table.end(); ++it) {
cout << it->first << ": " << it->second << endl;
}

return 0;
}

In this example, we declare a hash table hash_table with string keys and integer values using the
unordered_map class. We then insert three elements into the hash table using the [] operator. We
can access elements in the hash table using the same [] operator. Finally, we iterate over the
elements in the hash table using an iterator.
The unordered_map class provides many other methods for working with hash tables, including
find to search for an element by key, erase to remove an element by key, and size to get the number
of elements in the hash table.
Hash tables are a powerful data structure that can be used to implement a variety of algorithms and
applications. They are commonly used for caching, indexing, and searching, and are a key
component of many modern databases and programming languages.

2.9. Pointers and references


Pointers and references are important concepts in C++ that are used to manipulate memory and
improve the efficiency of programs.

pg. 34
35 CoE 135 Object Oriented Programming and Data Structure

A pointer is a variable that stores the memory address of another variable. It allows you to access
and modify the value of the variable indirectly by using the address. Here's an example of how to
use pointers in C++:
int main() {
int x = 10;
int *p; // declare a pointer to an integer
p = &x; // assign the address of x to p
cout << "The value of x is: " << x << endl;
cout << "The value of p is: " << p << endl;
cout << "The value pointed by p is: " << *p << endl;
*p = 20; // change the value of x using p
cout << "The new value of x is: " << x << endl;
return 0;
}

In this example, we declare an integer variable x and a pointer variable p that points to an integer.
We assign the address of x to p using the & operator. We can access the value of x indirectly using
p and the * operator. We can also modify the value of x indirectly by assigning a new value to the
memory location pointed to by p.
A reference is an alias to an existing variable. It allows you to access and modify the value of the
variable directly without using pointers. Here's an example of how to use references in C++:
int main() {
int x = 10;
int &r = x; // declare a reference to an integer
cout << "The value of x is: " << x << endl;
cout << "The value of r is: " << r << endl;
r = 20; // change the value of x using r
cout << "The new value of x is: " << x << endl;
return 0;
}

In this example, we declare an integer variable x and a reference variable r that refers to x. We can
access and modify the value of x directly using r. When we assign a new value to r, the value of x
is also updated.

pg. 35
36 CoE 135 Object Oriented Programming and Data Structure

In general, pointers are more powerful and flexible than references because they allow you to
manipulate memory directly. However, references are often preferred in C++ because they are
safer and more convenient to use, and can make the code easier to read and understand.

2.10. The Strategies for choosing the right data structure:


Choosing the right data structure is essential for building efficient and scalable programs. Here are
some strategies you can use to select the appropriate data structure for your project:
Understand your data: You need to have a clear understanding of the type of data you will be
working with. For example, if you are working with a large amount of numerical data, an array or
a matrix may be the best data structure. If you are working with text data, a string or a tree may be
more suitable.
Consider the operations you need to perform: Think about the operations you will be
performing on your data. For example, if you need to frequently insert or delete items from your
data, a linked list or a hash table may be more efficient than an array.
Consider the time and space complexity: Different data structures have different time and space
complexity. You should consider the trade-offs between time and space when selecting a data
structure. For example, a hash table may have a constant time complexity for inserting and
retrieving items, but it may require more memory than a linked list.
Consider the language and libraries you are using: Some programming languages and libraries
have built-in data structures that are optimized for specific tasks. For example, Python has a built-
in dictionary data type that is optimized for key-value lookups.
Test and benchmark your code: Once you have selected a data structure, it's important to test
and benchmark your code to make sure it's performing as expected. You may need to iterate and
try different data structures to find the most efficient one for your use case.

pg. 36
37 CoE 135 Object Oriented Programming and Data Structure

By following these strategies, you can select the most appropriate data structure for your project
and build efficient and scalable programs.

pg. 37
38 CoE 135 Object Oriented Programming and Data Structure

3. Database systems:
3.1. Introduction:
A database is a collection of data that is organized in a way that allows for efficient storage,
retrieval, and management of data. It is a crucial component in computer engineering as it provides
a reliable and scalable way of storing and retrieving data.
In computer engineering, databases are used to store large amounts of structured or unstructured
data in a way that allows for easy querying and manipulation of data. Databases can be used for a
variety of purposes, such as managing customer information, tracking inventory, and processing
financial transactions.
Databases can be classified into different categories based on their structure, such as relational
databases, NoSQL databases, and object-oriented databases. Relational databases are the most
common type of database used in computer engineering, and they store data in tables with a defined
set of relationships between them.
In addition to storing and retrieving data, databases also provide mechanisms for data security,
backup and recovery, and data integrity. Computer engineers must have a strong understanding of
databases and how to design and manage them effectively to ensure that the data is secure,
accurate, and easily accessible.

pg. 38
39 CoE 135 Object Oriented Programming and Data Structure

3.2. The Database components:


A database typically consists of several components, including:
Data: This is the raw information that is stored in the database. It can be structured, semi-structured
or unstructured data.
Tables: Tables are used to store and organize data in a relational database. They consist of rows
and columns, with each row representing a record and each column representing a field or attribute.
Fields/Attributes: Fields or attributes are the individual data elements within a table. They
represent a specific piece of information about an entity, such as a customer name, address or order
number.
Records: Records are the rows in a table that contain information about a specific entity or item.
Each record typically contains information for all of the fields or attributes within a table.
Relationships: Relationships are the links between tables in a relational database. They define
how the data in one table relates to the data in another table, and are typically represented by
foreign keys.
Indexes: Indexes are used to improve the performance of database queries. They provide a quick
way to locate data within a table based on specific criteria.
Queries: Queries are used to retrieve data from a database based on specific search criteria. They allow
users to filter, sort, and group data in a variety of ways.

Forms and Reports: Forms and reports are used to present data in a user-friendly way. Forms allow
users to input data into a database, while reports provide a way to view and analyze data in a
meaningful way.

pg. 39
40 CoE 135 Object Oriented Programming and Data Structure

These are some of the key components of a database, and understanding their roles and interactions is
crucial for designing, building and maintaining a successful database system.

3.1 Database Management System (DBMS)


A Database Management System (DBMS) is a software system that enables users to store,
organize, manage and retrieve data in a database. It is an essential tool for managing large amounts
of data, and it provides an efficient and effective way to store, access, and manipulate data.
A DBMS typically includes several components, including:
Data Definition Language (DDL): DDL is used to define the structure and organization of the
database, including creating tables and defining their relationships.
Data Manipulation Language (DML): DML is used to insert, update, and delete data in the
database.

Query Language: A query language is used to retrieve data from the database based on specific
criteria.

pg. 40
41 CoE 135 Object Oriented Programming and Data Structure

Transaction Management: Transaction management ensures the integrity of the database by


providing features like rollback and commit.
Security Management: Security management ensures the security of the database by providing
features like access control and user authentication.
Backup and Recovery: Backup and recovery features allow users to create backups of the database
and recover lost data in the event of a system failure.
DBMS systems can be classified into different types, including relational, object-oriented, NoSQL,
and graph databases. The choice of DBMS system depends on the specific needs of the application
and the data being stored.
DBMS is an essential component of modern information systems, and it is used in a wide range of
applications, including e-commerce, healthcare, finance, and education.
3.2 Database architectures:
There are several different database architectures that can be used depending on the specific needs
of an application. Here are some of the most common database architectures:
Relational Database Architecture: A relational database architecture stores data in tables that
are related to one another. The relationships between the tables are defined by primary and foreign
keys. Relational databases are widely used and are the most popular type of database architecture.

pg. 41
42 CoE 135 Object Oriented Programming and Data Structure

Object-Oriented Database Architecture: An object-oriented database architecture stores data as


objects, which are instances of classes. Object-oriented databases are useful for storing complex
data structures and for applications that require a high degree of flexibility.

NoSQL Database Architecture: A NoSQL database architecture is a non-relational database that


is designed to handle large volumes of unstructured or semi-structured data. NoSQL databases are
useful for applications that require high scalability and flexibility.

pg. 42
43 CoE 135 Object Oriented Programming and Data Structure

In-Memory Database Architecture: An in-memory database architecture stores data in RAM,


which provides faster access times than disk-based databases. In-memory databases are useful for
applications that require high-speed data processing.

Distributed Database Architecture: A distributed database architecture stores data across


multiple physical locations, which provides higher availability and better scalability. Distributed
databases are useful for applications that require high availability and low latency.

pg. 43
44 CoE 135 Object Oriented Programming and Data Structure

Graph Database Architecture: A graph database architecture stores data as nodes and edges,
which represent relationships between entities. Graph databases are useful for applications that
require complex relationship analysis, such as social networks or recommendation systems.

pg. 44
45 CoE 135 Object Oriented Programming and Data Structure

The choice of database architecture depends on the specific needs of an application, including the
volume and complexity of data, the required scalability and availability, and the desired query
performance.
3.3 Database architecture concept:
Database architecture refers to the overall design and structure of a database system. It
encompasses the different components of a database, including the data model, storage structures,
access methods, and data manipulation language.
There are several different database architecture concepts that are important to understand when
designing and managing a database system. Here are some of the most important ones:
Data Model: A data model is a representation of the data and the relationships between different
data elements in a database. The most commonly used data models include the relational model,
the object-oriented model, and the NoSQL model.

pg. 45
46 CoE 135 Object Oriented Programming and Data Structure

Storage Structures: The storage structures of a database refer to the way in which data is
physically stored on a disk or other storage device. Different types of storage structures include
tables, indexes, and views.
Access Methods: Access methods refer to the way in which data is retrieved and manipulated
from the database. The most commonly used access methods include SQL (Structured Query
Language), ODBC (Open Database Connectivity), and JDBC (Java Database Connectivity).

Data Manipulation Language (DML): DML refers to the way in which data is manipulated in a
database. Common DML operations include inserting, updating, and deleting data.

pg. 46
47 CoE 135 Object Oriented Programming and Data Structure

Transaction Management: Transaction management refers to the way in which transactions are
handled in a database. Transactions ensure that data is consistent and accurate, even if multiple
users are accessing the same data at the same time.

Query Optimization: Query optimization refers to the way in which queries are optimized for
maximum performance. Query optimization techniques include indexing, caching, and
partitioning.

pg. 47
48 CoE 135 Object Oriented Programming and Data Structure

Backup and Recovery: Backup and recovery refers to the way in which data is backed up and
restored in the event of a system failure or other disaster.

pg. 48
49 CoE 135 Object Oriented Programming and Data Structure

Understanding these database architecture concepts is essential for designing and managing a
successful database system. It involves careful planning, analysis, and optimization to ensure that
the system meets the requirements of the application and the users.

3.4 Database data independence:


Data independence refers to the ability to change the data storage or data model without affecting
the application programs that use the data. There are two types of data independence: physical data
independence and logical data independence.

Physical Data Independence: Physical data independence refers to the ability to change the
physical storage characteristics of data without affecting the logical schema of the database. For
example, a database administrator might need to move data from one storage device to another or
change the file format used to store the data. With physical data independence, the application
programs accessing the data do not need to be modified.
Logical Data Independence: Logical data independence refers to the ability to change the logical
schema of the database without affecting the application programs that use the data. For example,
a database designer might need to add new tables, modify existing tables, or change the
relationships between tables. With logical data independence, the application programs accessing
the data do not need to be modified as long as they use the same logical schema.

pg. 49
50 CoE 135 Object Oriented Programming and Data Structure

Data independence is an important concept in database design because it allows for greater
flexibility and scalability in the database system. Changes to the physical or logical schema can be
made without affecting the applications that use the data, which reduces the risk of data loss or
application downtime. It also allows for easier maintenance and upgrades of the database system.
3.5 Query:
A database query is a request for data or information from a database. It is a statement written in
a programming language, typically SQL (Structured Query Language), that is executed against a
database to retrieve data that matches certain criteria.

Here is an example of a simple SQL query that retrieves all records from a table called "customers":

SELECT * FROM customers;

This query selects all columns from the "customers" table and retrieves all records.
SQL queries can also be more complex and involve conditions, joins, and other operations to filter
and manipulate data. Here are some examples:
-- Retrieve all customers with a last name of "Smith"

pg. 50
51 CoE 135 Object Oriented Programming and Data Structure

SELECT * FROM customers WHERE last_name = 'Smith';

-- Retrieve all orders for a particular customer


SELECT * FROM orders WHERE customer_id = 123;

-- Retrieve the total revenue by product category


SELECT product_category, SUM(price * quantity) AS total_revenue
FROM order_items
GROUP BY product_category;

-- Retrieve all customers and their orders using a join


SELECT c.*, o.*
FROM customers c
JOIN orders o ON c.customer_id = o.customer_id;

Queries can be used to retrieve data, update data, insert new data, or delete data from a database.
They are an essential tool for working with databases and retrieving information in a structured
and efficient manner.

pg. 51
52 CoE 135 Object Oriented Programming and Data Structure

4. Data modeling:
Data modeling is the process of creating a visual representation of data and the relationships
between different data entities. It involves identifying the data that needs to be stored, defining the
relationships between different data entities, and creating a blueprint or schema that outlines the
structure of the data.
Data modeling is an essential part of database design and is used to ensure that data is organized
in a logical and efficient manner. It helps to eliminate redundancy and inconsistency in data storage
and ensures that data is accurate and easily accessible.
There are different types of data models, including conceptual, logical, and physical data models.
Conceptual models provide a high-level view of data and the relationships between entities.
Logical models provide more detailed information about the structure of the data and how it will
be stored. Physical models provide information about the actual implementation of the data,
including the database tables and fields.
Data modeling is used in a variety of applications, including database design, software
development, and business analysis. It is a critical step in the development of any information
system and helps to ensure that data is organized in a way that supports the needs of users and the
business.
4.1 Keys:
In the context of data modeling, a key is a field or set of fields that is used to identify a record or
a set of records in a database table. Keys are used to enforce data integrity and to help ensure that
data is unique and accurate.

There are several types of keys in data modeling:


Primary key: A primary key is a field or set of fields that uniquely identifies each record in a
table. The primary key is used to enforce data integrity and ensure that each record is unique. In

pg. 52
53 CoE 135 Object Oriented Programming and Data Structure

most databases, the primary key is also used as the basis for creating relationships between
tables.
Foreign key: A foreign key is a field or set of fields in a table that refers to the primary key of
another table. Foreign keys are used to establish relationships between tables and to ensure that
data is consistent between related tables.

Candidate key: A candidate key is a field or set of fields that could potentially be used as a primary
key. A table can have multiple candidate keys, but only one primary key can be selected.

Unique key: A unique key is a field or set of fields that must contain unique values, but is not
necessarily the primary key of a table.

pg. 53
54 CoE 135 Object Oriented Programming and Data Structure

Keys play a critical role in data modeling as they help to ensure that data is accurate, consistent,
and meaningful. By using keys to establish relationships between tables, it is possible to create
complex queries that can extract valuable insights from large datasets.
4.2 Foreign Key:
In data modeling, a foreign key is a column or a set of columns in a table that refers to the primary
key of another table. The purpose of a foreign key is to establish a link between two tables,
indicating that there is a relationship between the data in those tables.
Foreign keys are used to enforce referential integrity in a database, which ensures that data is
consistent and accurate. When a foreign key is defined in a table, the values in that column must
correspond to values in the primary key column of the referenced table. This means that a foreign
key cannot contain values that do not exist in the primary key column of the referenced table.
For example, consider a database with two tables: Orders and Customers. The Orders table has a
foreign key column called CustomerID, which refers to the primary key column of the Customers
table. This establishes a relationship between the Orders and Customers tables, indicating that each
order is associated with a specific customer.
When a new order is added to the Orders table, the value in the CustomerID column must
correspond to a value in the primary key column of the Customers table. If a value that does not
exist in the Customers table is entered into the CustomerID column, the database will generate an
error, preventing the insertion of invalid data.
Overall, foreign keys are an important concept in data modeling as they help to maintain data
integrity and consistency in a database. They also help to establish relationships between tables,
allowing for more complex and meaningful queries to be performed on the data.

4.3 Record:
A data modeling record, also known as a data model record or data model documentation, is a
detailed description of a data model. It typically includes information such as:

pg. 54
55 CoE 135 Object Oriented Programming and Data Structure

Entity types: The record lists all the entity types in the data model, including their attributes and
relationships with other entity types.
Relationship types: The record provides a description of the relationship types between the entity
types, including the cardinality and optionality of the relationships.
Business rules: The record documents any business rules that apply to the data model, such as
data validation rules or constraints.
Data constraints: The record outlines any constraints on the data, such as data type, length, and
format.
Data access: The record specifies how the data can be accessed, including the queries, reports,
and views that can be used to access the data.
Metadata: The record includes metadata about the data, such as the date of creation, the author,
and the purpose of the data model.
Data modeling records are used to communicate the design of the data model to other members of
the development team, stakeholders, and users. They help ensure that everyone has a clear
understanding of the data model and how it works, which can reduce errors, improve efficiency,
and increase the likelihood of success for the project.
4.4 Relation:
In data modeling, a relation refers to the association between two or more entities in a data model.
It is often represented in a database using a table. The term "relation" is often used interchangeably
with "table".

pg. 55
56 CoE 135 Object Oriented Programming and Data Structure

A relation in a data model is composed of a set of attributes, which are the properties or
characteristics of the entity, and the relationships between the entities. Each attribute in a relation
has a data type, such as text, number, or date, and a domain, which specifies the range of allowable
values.
The relationships between the entities in a relation are represented by keys. A key is a unique
identifier for a row in a table. A primary key is a special type of key that uniquely identifies each
row in a table. A foreign key is a column or set of columns in a table that refers to the primary key
of another table.
Data modeling is used to ensure that relations are properly defined and that the relationships
between the entities are accurately represented. This helps to ensure that the data is organized in a
logical and efficient manner and that it can be easily queried and analyzed.
4.5 Conceptual models:
Conceptual models are a type of data model used in data modeling that provide a high-level
overview of the entities and relationships in a system or business domain. They are typically
created early in the development process to help stakeholders and developers understand the
requirements of the system.
Conceptual models focus on the business objects or concepts involved in a system, rather than the
technical details of how the data will be stored or accessed. They are often represented using
diagrams such as entity-relationship diagrams (ERDs), which illustrate the relationships between
different entities.
A conceptual model may include the following components:
Entities: The key objects or concepts involved in the system, such as customers, orders, products,
or invoices.
Attributes: The characteristics or properties of each entity, such as name, address, price, or
quantity.
Relationships: The associations or connections between different entities, such as "one-to-many"
or "many-to-many" relationships.
Cardinality: The number of instances of one entity that can be associated with instances of another
entity.
Business rules: The constraints or requirements that apply to the data, such as validation rules,
calculation rules, or default values.
Conceptual models provide a high-level overview of the data requirements of a system, which can
help stakeholders and developers to identify potential issues or opportunities for improvement.
They can also be used to communicate the requirements of the system to other stakeholders, such
as business analysts or end-users.
Conceptual models offer several possibilities, including:

pg. 56
57 CoE 135 Object Oriented Programming and Data Structure

Requirements gathering: Conceptual models help in gathering and understanding the business
requirements of a system or application. It provides stakeholders with a common language and
understanding of the entities and relationships involved, helping to identify potential issues and
opportunities for improvement.
Communication: Conceptual models provide a visual representation of the system, which makes
it easier to communicate the requirements to stakeholders, including business analysts, developers,
and end-users. It helps stakeholders to understand the data structures, entities, and relationships
involved in the system.
Design and planning: Conceptual models help in designing and planning the system or application,
providing a framework for organizing data, identifying constraints, and developing relationships.
It provides a blueprint for the system design and helps in deciding how the data will be stored,
organized, and accessed.
Testing and validation: Conceptual models help in testing and validating the system or application.
It provides a framework for defining test scenarios and validating data, helping to ensure that the
system meets the requirements and functions as expected.
Maintenance and enhancement: Conceptual models provide a reference point for maintaining and
enhancing the system or application. It helps in identifying the impact of changes, ensuring that
the system continues to function as intended and minimizing the risk of errors and inconsistencies.
Overall, conceptual models are a valuable tool for understanding, designing, and developing
systems and applications. They help stakeholders to communicate, collaborate, and make informed
decisions about the system's requirements and design.
4.6 Unified Modeling Language (UML):
Unified Modeling Language (UML) is a modeling language used in software engineering to
design, visualize, and document software systems. It includes a set of graphical notations for
representing various aspects of a system, including its structure, behavior, and interactions. UML
can be used to create conceptual models, among other types of models.

pg. 57
58 CoE 135 Object Oriented Programming and Data Structure

UML provides several diagram types that can be used to create conceptual models, including:
Class diagrams: These diagrams show the classes and relationships between them in a system.
They illustrate the entities and their attributes, methods, and associations.
Use case diagrams: These diagrams depict the actors and their interactions with the system. They
illustrate the various scenarios or use cases in which the system is used.
Object diagrams: These diagrams show a snapshot of a system at a specific point in time, focusing
on the objects and their relationships.
Package diagrams: These diagrams illustrate the organization of the system into packages or
modules, showing the dependencies between them.
Component diagrams: These diagrams show the components and their interactions within a
system, illustrating how the system is partitioned into components.
UML is a widely used modeling language, and its diagrams provide a standardized way to create
conceptual models. They can help in communicating the system requirements, design, and
behavior to stakeholders, developers, and other team members. UML models are also useful for
generating code, testing, and maintaining the system over time.
UML has several strengths and weaknesses, including:
Strengths:
Standardization: UML is an internationally recognized standard for modeling software systems.
This means that it is widely accepted and understood by software developers, making it easier to
communicate and collaborate on software development projects.
Flexibility: UML offers a wide range of diagrams and notations, allowing developers to model
different aspects of a software system. This means that UML can be used in different stages of
software development, from requirements gathering to system design, testing, and maintenance.
Visual representation: UML uses visual diagrams to represent software systems, making it easier
for stakeholders to understand and review the system's requirements, design, and behavior. This
makes it easier to identify potential issues and make informed decisions about the system
Reusability: UML models can be used to generate code automatically, saving time and effort in
software development. This is especially useful for generating code for repetitive tasks or for code
that is error-prone.
Weaknesses:
Complexity: UML can be complex and difficult to learn, especially for beginners. There are many
notations and diagrams, and it can be challenging to choose the appropriate diagram and notation
for a particular task.
Cost: UML modeling tools can be expensive, which can be a barrier to adoption for small software
development teams or individual developers.

pg. 58
59 CoE 135 Object Oriented Programming and Data Structure

Incomplete: UML is not a complete software development methodology, and it does not provide
guidance on many important aspects of software development, such as project management,
quality assurance, or testing.
Misuse: UML can be misused or overused, leading to diagrams that are overly complex or difficult
to understand. This can result in confusion and miscommunication among stakeholders, and can
lead to mistakes in software development.
Overall, UML is a useful modeling language for software development, but it requires careful use
and consideration of its strengths and weaknesses.
4.7 Object-oriented modeling (OOM):
Object-oriented modeling (OOM) is a software engineering technique used to represent the
behavior and structure of a system or application. In object-oriented modeling, a system is
represented as a collection of objects, each with its own data and behavior.
The key components of object-oriented modeling include:
Objects: Objects are the basic building blocks of a system in object-oriented modeling. They
represent entities in the system, such as customers, orders, or products. Each object has its own
data and behavior, and interacts with other objects in the system through messages.
Classes: Classes are templates for creating objects. They define the data and behavior of the
objects, including attributes and methods. Classes can be used to create multiple instances of
objects with similar data and behavior.
Inheritance: Inheritance is a mechanism that allows classes to inherit data and behavior from other
classes. This helps to create more efficient and reusable code by reducing duplication of code.
Polymorphism: Polymorphism is the ability of objects to take on different forms or behaviors in
different contexts. This allows for more flexible and adaptable software systems.
Encapsulation: Encapsulation is the principle of hiding the internal workings of an object from
the outside world. This helps to reduce complexity and increase the reliability and maintainability
of the system.
Object-oriented modeling has several benefits, including:
Reusability: Object-oriented modeling promotes code reuse by creating classes and objects that
can be used in different parts of the system or in different projects.
Modularity: Object-oriented modeling helps to break down complex systems into smaller, more
manageable modules, making it easier to design, implement, test, and maintain software.
Flexibility: Object-oriented modeling allows for flexible and adaptable software systems that can
be easily modified or extended to meet changing business requirements.
Maintainability: Object-oriented modeling promotes the use of best practices, such as
encapsulation and inheritance, that improve the maintainability and reliability of software systems.

pg. 59
60 CoE 135 Object Oriented Programming and Data Structure

Overall, object-oriented modeling is a powerful technique for software engineering that allows for
more efficient, flexible, and maintainable software systems.

pg. 60
61 CoE 135 Object Oriented Programming and Data Structure

5. Structured Query Language (SQL):


SQL (Structured Query Language) is a programming language used to manage and manipulate
relational databases.

Here are some fundamental concepts of SQL:


Database: A database is a collection of tables, each containing rows and columns of data. It is
used to organize, store and manage data.
Table: A table is a collection of related data stored in rows and columns. Each column represents
a different attribute or characteristic, while each row represents a specific instance or record of the
data.
Query: A query is a command or request that retrieves data from one or more tables in a database.
It can be used to filter, sort, group, or join data.
Select statement: A select statement is a query that retrieves data from one or more tables in a
database. It can be used to retrieve specific columns or all columns, filter data using conditions,
and sort data.
Condition: A condition is a logical expression used to filter data in a query. It can be used to
compare values using operators such as equals, not equals, greater than, less than, and others.
Join: A join is a query that combines data from two or more tables into a single result set based
on a common column or key.

pg. 61
62 CoE 135 Object Oriented Programming and Data Structure

Primary key: A primary key is a unique identifier for each row in a table. It is used to ensure that
each row is unique and can be used to join tables together.
Foreign key: A foreign key is a column in one table that references the primary key in another
table. It is used to create relationships between tables.
Index: An index is a data structure used to improve the performance of queries by allowing data
to be retrieved more quickly.
Transaction: A transaction is a sequence of database operations that are treated as a single unit of
work. It ensures that all changes are made or none at all, maintaining the integrity of the data.
6.1 Data Definition Language (DDL):
In SQL, data definition language (DDL) statements are used to create, modify, and delete database
objects such as tables, indexes, views, and constraints.

Here are some common SQL DDL statements used for data definition:
CREATE TABLE statement: The CREATE TABLE statement is used to create a new table in a
database. It specifies the name of the table, its columns, and their data types, as well as any
constraints or indexes.
Example:
CREATE TABLE employees (
id INT PRIMARY KEY,
name VARCHAR(50),
age INT,
department VARCHAR(50)
);

ALTER TABLE statement: The ALTER TABLE statement is used to modify an existing table in
a database. It can be used to add or remove columns, modify data types, and add or remove
constraints.
Example:

pg. 62
63 CoE 135 Object Oriented Programming and Data Structure

ALTER TABLE employees


ADD salary INT;

DROP TABLE statement: The DROP TABLE statement is used to delete an existing table from
a database.
Example:
DROP TABLE employees;

CREATE INDEX statement: The CREATE INDEX statement is used to create an index on one
or more columns of a table. It can be used to improve the performance of queries that search for
data in the indexed columns.
Example:
CREATE INDEX idx_employees_department ON employees (department);

CREATE VIEW statement: The CREATE VIEW statement is used to create a virtual table based
on the result of a SELECT statement. It can be used to simplify complex queries or provide a
simplified view of the data in a table.
Example:
CREATE VIEW v_employees AS
SELECT name, age, department FROM employees;

CONSTRAINT statement: The CONSTRAINT statement is used to define rules for the data
stored in a table. It can be used to enforce data integrity, such as ensuring that a column cannot
have null values or that the values in a column must be unique.
Example:
CREATE TABLE employees (
id INT PRIMARY KEY,
name VARCHAR(50),
age INT,
department VARCHAR(50),
CONSTRAINT unique_name UNIQUE (name)
);

pg. 63
64 CoE 135 Object Oriented Programming and Data Structure

6.2 SQL query formulation:


SQL query formulation involves the creation of statements that retrieve data from a database using
various operations such as selecting, filtering, sorting, joining, and aggregating. Here are some key
steps to follow when formulating a SQL query:
Determine the goal of the query: Start by identifying the specific data you want to retrieve from
the database. This will help you decide which tables and columns to use, and what conditions to
apply.
Select the appropriate tables: Choose the tables that contain the data you need for the query.
Make sure the tables are related and have a common field that can be used for joining.
Choose the appropriate columns: Select the columns that contain the data you need for the query.
Be specific about the columns you want to retrieve to minimize the amount of data returned.
Apply filtering conditions: Use the WHERE clause to specify the filtering conditions for the
query. This will help you retrieve only the data that meets the specified criteria.
Sort the results: Use the ORDER BY clause to sort the results of the query based on one or more
columns.
Join tables: Use the JOIN clause to combine data from multiple tables. Choose the appropriate
type of join (INNER, LEFT, RIGHT, or FULL) based on the relationship between the tables.
Group and aggregate data: Use the GROUP BY clause to group the results of the query based
on one or more columns. Use aggregate functions such as COUNT, SUM, AVG, MIN, or MAX
to calculate summary values for each group.
Limit the results: Use the LIMIT clause to limit the number of rows returned by the query.
Here is an example SQL query that demonstrates these steps:
SELECT e.name, COUNT(*) as num_orders
FROM employees e
JOIN orders o ON e.id = o.employee_id
WHERE o.order_date >= '2022-01-01'
GROUP BY e.name
ORDER BY num_orders DESC
LIMIT 10;

This query retrieves the names of the top 10 employees who have the most orders since January
1st, 2022. It joins the employees and orders tables, filters the orders by date, groups the results by
employee name, calculates the count of orders for each employee, and sorts the results by the count
in descending order. The LIMIT clause limits the number of rows returned to 10.

pg. 64
65 CoE 135 Object Oriented Programming and Data Structure

6.3 Update sub-language:


SQL's update sub-language is used to modify existing data in a table. It is part of the Data
Manipulation Language (DML) and is used to make changes to individual rows or sets of rows in
a table. Here are the key elements of the SQL update sub-language:
UPDATE statement: The UPDATE statement is used to modify data in a table. It specifies the
name of the table and the new values for one or more columns.
Example:
UPDATE employees
SET salary = 50000
WHERE department = 'Sales';

This statement sets the salary of all employees in the Sales department to 50,000.
SET clause: The SET clause is used to specify the new values for one or more columns. Each
column and its new value are separated by an equals sign.
Example:
UPDATE employees
SET salary = 50000, commission = 0.1
WHERE department = 'Sales';

This statement sets the salary to 50,000 and commission to 0.1 for all employees in the Sales
department.
WHERE clause: The WHERE clause is used to specify the conditions that must be met for the
update to be applied. Only the rows that satisfy the conditions will be updated.
Example:
UPDATE employees
SET salary = salary * 1.1
WHERE department = 'Marketing' AND experience >= 5;

This statement increases the salary of all employees in the Marketing department with at least 5
years of experience by 10%.
UPDATE with subquery: It is also possible to use a subquery in the UPDATE statement to update
rows based on the results of a subquery.
Example:
UPDATE employees
SET department = 'Sales'
WHERE id IN (
SELECT employee_id FROM orders

pg. 65
66 CoE 135 Object Oriented Programming and Data Structure

WHERE order_date >= '2022-01-01'


);

This statement sets the department of all employees who have orders since January 1st, 2022 to
Sales.
Note that the update sub-language should be used with caution, as it can have a significant impact
on the integrity and consistency of the database. Always make sure to test and verify the results of
any updates before committing them.
6.4 SQL constraints:
SQL constraints are rules that are used to enforce certain conditions on the data in a database.
These rules are designed to ensure data integrity and prevent invalid or inconsistent data from
being inserted, updated, or deleted.

Here are some of the most common SQL constraints:


NOT NULL constraint: The NOT NULL constraint is used to ensure that a column does not
contain null values. It is used to enforce mandatory data entry.
Example:
CREATE TABLE employees (
id INT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
age INT NOT NULL,
email VARCHAR(100) UNIQUE
);

pg. 66
67 CoE 135 Object Oriented Programming and Data Structure

This table definition specifies that the name and age columns cannot be null, while the email
column must contain unique values.
UNIQUE constraint: The UNIQUE constraint is used to ensure that each value in a column is
unique. It is used to enforce uniqueness and prevent duplicate entries.
Example:
CREATE TABLE products (
id INT PRIMARY KEY,
name VARCHAR(50) UNIQUE,
price DECIMAL(10,2)
);

This table definition specifies that the name column must contain unique values.
PRIMARY KEY constraint: The PRIMARY KEY constraint is used to identify a unique row in
a table. It is used to enforce data uniqueness and facilitate data retrieval.
Example:
CREATE TABLE customers (
id INT PRIMARY KEY,
name VARCHAR(50),
email VARCHAR(100)
);

This table definition specifies that the id column is the primary key for the customer’s table.
FOREIGN KEY constraint: The FOREIGN KEY constraint is used to establish a link between
two tables based on a common column. It is used to enforce referential integrity and maintain data
consistency.
Example:
CREATE TABLE orders (
id INT PRIMARY KEY,
customer_id INT,
product_id INT,
quantity INT,
FOREIGN KEY (customer_id) REFERENCES customers(id),
FOREIGN KEY (product_id) REFERENCES products(id)
);

This table definition specifies that the orders table has foreign keys that reference the customers
and products tables.
CHECK constraint: The CHECK constraint is used to ensure that a value in a column meets a
specific condition. It is used to enforce data validity and prevent invalid entries.

pg. 67
68 CoE 135 Object Oriented Programming and Data Structure

Example:
CREATE TABLE employees (
id INT PRIMARY KEY,
name VARCHAR(50),
age INT,
salary DECIMAL(10,2),
CHECK (age >= 18),
CHECK (salary >= 0)
);

This table definition specifies that the age column must be at least 18, and the salary column must
be non-negative.
SQL constraints are important for maintaining data integrity and consistency in a database. They
can help prevent data errors and ensure that the data in a database is accurate and reliable.
6.5 SQL integrity:
SQL integrity refers to the consistency, accuracy, and reliability of data in a database. Maintaining
data integrity is important for ensuring that the data in a database is valid and meaningful, and that
it can be used effectively for data analysis and decision-making.

There are several types of SQL integrity that are essential for maintaining data quality:
Entity integrity: Entity integrity ensures that each row in a table is unique and identifiable. This
is usually enforced through the use of a primary key constraint, which ensures that each row in a
table has a unique identifier.
Referential integrity: Referential integrity ensures that relationships between tables are
maintained correctly. This is usually enforced through the use of foreign key constraints, which
ensure that rows in one table that are related to rows in another table are linked correctly.
Domain integrity: Domain integrity ensures that the values in a column are valid and appropriate
for that column. This is usually enforced through the use of data type constraints, which ensure
that each column contains data of a specific type.
User-defined integrity: User-defined integrity ensures that business rules and other constraints
are enforced on the data in a database. This can be enforced through the use of CHECK constraints,

pg. 68
69 CoE 135 Object Oriented Programming and Data Structure

which allow the database administrator to specify custom rules that data must conform to in order
to be considered valid.
By enforcing these types of integrity in a database, it is possible to maintain the accuracy and
consistency of the data, prevent data errors and inconsistencies, and ensure that the data can be
used effectively for data analysis and decision-making.

pg. 69

You might also like