386 Java Developer Interview Questions & Answers

139 top • 34 Amazon • 36 Apple • 41 Google • 35 Meta • 39 Microsoft • 31 Netflix • 31 NVIDIA

Java Developer icon

Questions with Detailed ExplanationsWith Detailed Explanations

(Last Updated: August 28, 2026)

81. What is system design?NEWSystem DesignEasy

Question Details

Define system design as deciding how software components, data stores, interfaces, and infrastructure work together to meet stated requirements. Explain the beginner interview path: clarify users and scope, identify functional and non-functional requirements, estimate scale, define APIs and data, draw a simple high-level design, and then discuss bottlenecks, failures, security, observability, and tradeoffs.

Short Interview Answer (30-60 seconds)

At a high level, system design is deciding how software parts work together to meet real requirements. The main challenge is balancing features with scale, reliability, security, and cost. I would explain it in three parts: understand the problem, draw a simple high-level design, and then discuss risks and trade-offs. In this example, users reach stateless application services through a Load Balancer and API Gateway. Databases, Redis cache, a Message Queue, and external services support the design. The trade-off is usually simplicity versus flexibility and scale.

Detailed Explanation

System design means deciding how the main parts of a software system should work together. The goal is not only to make the features work. The system should also handle the expected number of users, stay reliable, protect data, and remain easy to operate. The difficult part is that every design choice affects something else. A faster design may cost more. A simpler design may be harder to scale later. The diagram gives a beginner-friendly path from understanding the problem to discussing those choices.

Useful Questions to Ask the Interviewer
  1. Who will use the system, and what should it do?
  2. What is inside the scope, and what is outside it?
  3. What traffic and data size should we expect?
  4. Which requirements matter most, such as speed, reliability, or security?
What is system design? diagram
How to Explain It in an Interview
1. Start with users, scope, and requirements

I would start by making sure I understand what we are building. First, I would identify the users and the product scope. Then I would separate functional requirements from non-functional requirements. Functional requirements describe features. Non-functional requirements describe qualities such as performance, security, and reliability. This gives the design a clear target before we choose components.

2. Estimate scale and define APIs and data

Next, I would estimate how large the system may become. The diagram suggests looking at users, requests per second, data growth, storage, and bandwidth. After that, I would define the APIs and the data model. An API is the interface clients use to send requests. The data model describes how information is stored and related. These decisions guide the high-level architecture.

3. Draw the high-level request flow

For the example web application, users connect through the Internet to a Load Balancer. The Load Balancer distributes incoming traffic. Requests then go to the API Gateway. It handles routing, rate limiting, authentication, and authorization. From there, the flow reaches stateless application services such as User Service, Order Service, and Payment Service. Stateless means a request does not depend on session data kept inside one service instance.

4. Connect services to data and background work

The application services use the User DB, Order DB, and Redis Cache shown in the diagram. The two databases are relational stores for application data. Redis is a cache that can make repeated access faster. The Message Queue supports asynchronous work, which means work that can happen outside the immediate request flow. The diagram also shows external services for email, payments, and SMS. These integrations should not make the whole system depend on one slow external call.

5. Discuss failures, security, observability, and trade-offs

Finally, I would explain what can go wrong and how the design responds. A database can become a bottleneck, so indexes, read replicas, and caching may help. A service may fail, so retries, timeouts, circuit breakers, and graceful degradation matter. Traffic spikes may be handled with autoscaling and queues. Observability means using logs, metrics, traces, and alerts to understand the system. Security includes authentication, authorization, TLS, and secrets. The main trade-offs shown are consistency versus availability, performance versus cost, simplicity versus flexibility, build versus buy, and short-term speed versus long-term maintainability.

Engineering Considerations / Design Trade-offs

The benefit of this design is that each part has a clear job. Stateless services can scale horizontally, which means adding more service instances. Caching can make common access faster. Queues can move some work away from the immediate request flow. The downside is more moving parts. More services, databases, caches, and queues create more operational work. Better performance can also cost more. Stronger availability may need extra infrastructure. We also have to choose between building something ourselves and buying a service. There is no perfect design. We choose the trade-offs that best match the requirements.

Why Interviewers Ask This

Interviewers ask this question to see how you think before choosing technology. They want to know whether you can clarify a problem, estimate scale, define APIs and data, and draw a simple architecture. They also want to see whether you notice bottlenecks, failures, security, observability, and trade-offs. A strong answer shows judgment and clear communication, not memorized component names.

Interviewer may ask next
How would this design change if traffic suddenly became ten times higher?

I would keep the same basic design, but I would scale the parts receiving the most traffic. The stateless application services are the easiest place to start. We can run more instances behind the Load Balancer. The API Gateway can still handle routing and rate limiting so one client cannot overwhelm the system.

I would also watch the databases and Redis Cache closely. If a database becomes the bottleneck, the diagram already suggests indexes, read replicas, and caching. Read replicas can move some read work away from the main database. Redis can reduce repeated database access.

The Message Queue can also help absorb short traffic spikes for background work. This keeps slower external work from placing as much pressure on the immediate request flow.

The downside is higher cost and more operational complexity. More instances, replicas, and cache capacity need monitoring and careful limits.

What would you do if one application service started failing repeatedly?

I would keep the same architecture and stop the failure from spreading. The application services are stateless, so a bad instance does not need to hold important session state. Traffic can be moved toward healthy instances while the failing one recovers.

For calls that may fail for a short time, I would use the reliability controls shown in the diagram. Timeouts stop one slow call from waiting forever. Limited retries can handle short failures. A circuit breaker can temporarily stop calls to a dependency that keeps failing. Graceful degradation means the system may provide less functionality instead of failing completely.

Logs, metrics, traces, and alerts help us find the cause and measure the impact. The Message Queue can keep suitable asynchronous work outside the immediate request flow.

The downside is added complexity. Poor retry settings can also create more traffic during an outage.

82. What is a microservice?NEWSystem DesignEasy

Question Details

Define a microservice as a small independently deployable service built around a focused business capability. Compare it with a modular monolith, and explain service boundaries, APIs or events, independent data ownership, deployment, scaling, observability, network failures, consistency, and operational cost. Make clear that microservices are a tradeoff rather than a default requirement.

Short Interview Answer (30-60 seconds)

At a high level, a microservice is a small service built around one business capability. The main challenge is gaining independent deployment and scaling without creating too much distributed-system complexity. I would explain it in three parts: service boundaries and data ownership, communication through APIs or events, and operational trade-offs. In this design, each service owns its database and can scale separately. The trade-off is more network failures, consistency work, monitoring, and operational cost.

Detailed Explanation

A microservice is a small application that handles one focused business job. For example, one service can manage orders while another handles payments. The hard part is not simply creating many small applications. They must have clear boundaries, own their data, communicate safely, and keep working when networks or other services fail. The diagram explains this through separate business services, independent databases, API and event communication, independent deployment and scaling, and the operational costs that come with those choices.

Useful Questions to Ask the Interviewer
  1. Do the business areas have clear boundaries and separate team ownership?
  2. Do some services need to scale more than others?
  3. How much consistency is required between different services?
What is a microservice? diagram
How to Explain It in an Interview
1. Start with the service boundary

I would say that each microservice should have one focused business responsibility. In the diagram, User Service manages users, Order Service manages orders, Payment Service handles payments, and Inventory Service manages inventory.

This boundary matters because each service can change and deploy independently. It also gives teams clearer ownership. A modular monolith is different because its modules run in one application and deploy together.

2. Explain requests and independent data ownership

Clients first send requests to the API Gateway. The gateway handles routing, authentication and authorization, and rate limiting. It then sends each request to the correct service.

Each service owns its own database. User Service uses User DB, Order Service uses Order DB, Payment Service uses Payment DB, and Inventory Service uses Inventory DB. The diagram shows SQL or JDBC access between each service and its database. This avoids one shared database becoming the ownership boundary for every service.

The Java example shows the same idea with a Spring Boot OrderController. The controller exposes a REST API and calls OrderService for the business logic.

3. Explain APIs and events

Services can expose APIs or use events for communication. An API is useful when one side needs a direct request and response. An event is useful when other work can happen separately.

The diagram shows event paths from the service and data side into the Message Broker, labeled Kafka or RabbitMQ. The broker then connects to the external Email Service and Shipping Service. This keeps event-based work separate from the main client request path.

4. Explain deployment, scaling, and observability

A major benefit is independent deployment. One service can be released without deploying every other service.

Services can also scale independently. If Payment Service has more load, we can scale that service based on its own needs. We do not need to scale the whole application.

The downside is that we need good observability. This means logs, metrics, tracing, and health checks that help us understand what happens across several services.

5. Explain failures, consistency, and the trade-off

Network calls can fail because services communicate across a network. The diagram suggests timeouts, retries, circuit breakers, and fallbacks for these failures.

Data in separate services may become consistent at slightly different times. This is called eventual consistency, which means services may briefly hold different views of a business process. The diagram also mentions sagas for transactions that span several service steps.

The main point is that microservices are not the default answer. They are useful when independent deployment, scaling, fault isolation, and team ownership are worth the extra complexity and operational cost.

Engineering Considerations / Design Trade-offs

The benefit is that each service can be deployed and scaled independently. Teams can also own clear business areas and separate databases. The downside is that communication happens over a network, so requests can fail or become slow. Data in different services may also agree at different times. We need logs, metrics, tracing, health checks, retries, timeouts, circuit breakers, and fallbacks. More services also mean more infrastructure and DevOps work. A modular monolith is simpler to operate. I would choose microservices only when their independence, scaling, and fault isolation are worth these extra costs.

Why Interviewers Ask This

The interviewer wants to see whether you understand microservices as a design trade-off, not just a definition. They want to know if you can choose clear service boundaries, keep data ownership separate, explain API and event communication, and think about scaling and network failures. They also want to see whether you know when a simpler modular monolith may be the better choice.

Interviewer may ask next
What would you do if one business operation needs updates in both Order Service and Payment Service?

I would keep Order Service and Payment Service separate, including their separate databases. I would not turn them into one shared database transaction. The diagram already points to sagas for work that spans several services.

A saga splits one business operation into smaller service steps. Each service changes only the data it owns. For example, Order Service can create its order state, while Payment Service handles payment state separately. Events can be published through the existing Message Broker when downstream work should happen without blocking the main request.

If a later step fails, the business flow needs a compensating action. That means undoing or correcting an earlier business step instead of rolling back one shared database transaction.

This keeps independent data ownership intact. The downside is more complexity. Different services may briefly show different states, so tracing, metrics, and clear failure handling become more important.

What would you do if Payment Service suddenly receives much more traffic than the other services?

I would scale Payment Service independently instead of scaling the whole system. That is one of the main benefits shown in the diagram. User Service, Order Service, and Inventory Service can stay at their current size if their load has not changed.

Payment Service would still own Payment DB and keep the same request and event relationships. The architecture does not need a shared database or one larger deployment containing every service. We add capacity only where the load exists.

I would also watch the logs, metrics, tracing, and health checks shown in the diagram. They help us confirm whether Payment Service is the real bottleneck and whether its network calls or database access are failing.

The downside is operational cost. Independent scaling gives more control, but it also requires more infrastructure, monitoring, and DevOps work.

83. What is a REST API?NEWAPI DesignEasy

Question Details

Define a REST API in practical HTTP terms. Explain resources and URLs, HTTP methods, representations such as JSON, status codes, stateless requests, validation, consistent errors, authentication, pagination, idempotency, and caching. Use one small resource example and distinguish REST from a Java framework or a transport protocol.

Short Interview Answer (30-60 seconds)

At a high level, a REST API lets clients work with resources through HTTP. For example, products use URLs such as /products and /products/123. The client sends methods like GET, POST, PUT, PATCH, or DELETE, and the server returns a representation such as JSON with a meaningful status code. Each request should contain what the server needs. I would also validate input, authenticate requests, return consistent errors, paginate large lists, and use caching when useful. REST is an architectural style, not a Java framework, HTTP itself, or a data format.

Detailed Explanation

A REST API gives programs a clear way to read or change information. In this example, the information is about products. A product gets an address such as /products/123. A client sends a request to that address, and the server sends a result back. The goal is to make these requests predictable and easy to understand. We also want invalid requests and failures to be handled clearly. I will follow the product example in the diagram and explain the URLs, actions, responses, security, errors, large lists, and caching.

Useful Questions to Ask the Interviewer
  • Which resources should this API expose?
  • Which operations should clients perform on those resources?
  • Which requests require authentication?
  • How large can product lists become?
What is a REST API? diagram
How to Explain It in an Interview
1. Start with resources and URLs

I would start with the resource. A resource is the thing the client wants to work with. Here, that resource is a product. The collection URL is /products. One specific product uses /products/123, where 123 identifies that product. The full example is https://api.example.com/products/123. The URL names the resource rather than an action. This makes the API easier for clients to understand.

2. Use HTTP methods for operations

The HTTP method tells the server what operation the client wants. GET /products reads the collection. GET /products/123 reads one product. POST /products creates a product. PUT /products/123 replaces a product. PATCH /products/123 partially updates it. DELETE /products/123 removes it. GET is safe because it should not change server state. GET, PUT, and DELETE are idempotent by HTTP semantics. Idempotent means repeating the same request has the same intended effect. The diagram also treats its PATCH operation as idempotent. PATCH is not automatically idempotent in HTTP, so that behavior must be designed carefully for this API. POST is shown as non-idempotent.

3. Make each request stateless

Each request should contain the information needed to process it. The server should not depend on stored client session state between requests. The example sends GET /products/123 HTTP/1.1 to api.example.com. It includes Accept: application/json and Authorization: Bearer <token>. HTTPS protects the request while it travels over the network. A bearer token or API key can identify or authenticate a caller. OAuth 2.0 can be used to obtain and authorize access tokens; it is not itself a Java framework.

4. Return representations and useful status codes

The server returns a representation of the resource. The example uses JSON. It returns HTTP/1.1 200 OK, Content-Type: application/json, and product fields such as id, name, price, and inStock. Common codes shown are 200 OK, 201 Created, 400 Bad Request, 401 Unauthorized, 404 Not Found, 409 Conflict, and 500 Internal Server Error. These codes help clients understand what happened.

5. Validate input and keep errors consistent

I would validate all input before using it. Invalid input should return 400 Bad Request with a clear message. Errors should use one consistent JSON shape so clients can handle them predictably. The diagram shows fields such as error, message, and code. Missing or invalid authentication can return 401 Unauthorized. A missing product returns 404 Not Found. A conflicting operation can return 409 Conflict. An unexpected server problem returns 500 Internal Server Error.

6. Paginate large lists and cache useful responses

Large product collections should be returned in pages. The diagram uses GET /products?page=2&size=20. A response can include a total count or next and previous links. Caching can reduce server work and improve speed. The diagram shows Cache-Control, ETag, and Last-Modified. Its sample product response uses Cache-Control: max-age=60, allowing that response to be reused for a limited time.

7. Explain what REST is not

Finally, REST is an architectural style. It is not a Java framework. Java frameworks such as Spring can help implement REST APIs, but they are not REST itself. REST commonly uses HTTP, but REST is not the HTTP transport protocol. REST is also not a data format. JSON is common, but XML, CSV, or other representations can also be used.

Practical Complexity & Trade-offs

The main design goal is predictability. Resource URLs make it clear what data the client is using. HTTP methods give operations familiar meanings. Status codes and consistent error bodies make failures easier to handle. Stateless requests simplify server handling because every request carries what it needs. Authentication protects access, but credentials and tokens must be managed safely. Pagination keeps large responses smaller, but clients need more requests. Caching can improve speed and reduce server work, but cached data may be older until its cache lifetime ends. Idempotent operations are easier to repeat after an uncertain network result. The benefit is safer recovery. The downside is that the API must define repeated updates carefully, especially because PATCH is not inherently idempotent.

Why Interviewers Ask This

Interviewers ask this question to check practical API judgment, not only REST vocabulary. They want to see whether the candidate can model resources clearly, choose correct HTTP methods, return meaningful status codes, validate requests, authenticate callers, and design consistent errors. They also look for understanding of stateless requests, pagination, caching, and idempotency. A strong answer also separates REST from Java frameworks, the HTTP transport protocol, and formats such as JSON.

Interviewer may ask next
What would you change if the products collection became very large?

I would keep the same resource design and use the pagination behavior already shown for GET /products. Instead of returning every product, the client would request a page such as GET /products?page=2&size=20. The server would return only that page and could include the total count or links to the next and previous pages, as shown in the diagram. This keeps each response smaller and reduces network, memory, and processing work. The existing authentication, validation, status codes, error format, and product URLs would stay unchanged. Caching can also help when clients repeatedly request the same product data. The main downside is that clients need more requests to move through a large collection. They must also keep track of page information. We accept that extra client work because returning one huge product list would become slower and more expensive as the collection grows.

How should the API handle repeated requests when the client is unsure whether the first request succeeded?

I would use the idempotency behavior of each operation rather than retrying every request blindly. GET, PUT, and DELETE are idempotent by HTTP semantics, so repeating the same request should have the same intended effect. For example, sending the same PUT /products/123 replacement again should leave product 123 in the same final state. The diagram also marks its PATCH /products/123 operation as idempotent, so this particular API must implement that partial update in a repeat-safe way. PATCH is not inherently idempotent in HTTP, so I would not assume this behavior for every API. POST /products is shown as non-idempotent. Repeating it could create another resource, so the client should not automatically retry it as if it were safe. Authentication, validation, errors, and status codes stay unchanged. The downside is that clients need to understand each operation's retry behavior.

84. What is Spring MVC?NEWAPI DesignEasy

Question Details

Define Spring MVC as Spring Framework's Servlet-based web framework. Explain DispatcherServlet, request mapping, controllers, method arguments, validation, HTTP message conversion, response bodies, exception handling, and the normal request-response path. Briefly distinguish Spring MVC from the non-blocking Spring WebFlux stack.

Short Interview Answer (30-60 seconds)

At a high level, Spring MVC is Spring Framework’s Servlet-based web framework for handling HTTP requests and responses. A request first reaches DispatcherServlet, which acts as the front controller. It uses request mapping to find the correct controller method. Spring resolves method arguments, validates input, runs the controller, and converts the result into HTML, JSON, XML, or text. Centralized exception handling keeps error responses consistent. The main trade-off is that Spring MVC uses a blocking, thread-based model, while Spring WebFlux uses a non-blocking reactive model for high-concurrency workloads.

Detailed Explanation

Spring MVC helps a Java application receive a web request, choose the correct code, and send a response back. The goal is to make this flow clear and organized. The main challenge is understanding how the request reaches the correct controller, how request values become Java method arguments, how those values are checked, and how the returned Java value becomes an HTTP response. I would explain the same request-response path shown in the diagram, starting with the client and ending when DispatcherServlet sends the response back.

Useful Questions to Ask the Interviewer
  • Do you want me to explain both REST responses and HTML views?
  • Should I also compare Spring MVC with Spring WebFlux?
What is Spring MVC? diagram
How to Explain It in an Interview
1. Start with DispatcherServlet

I would start by saying that Spring MVC is Servlet-based. The client sends an HTTP request such as GET /users/123. DispatcherServlet receives the request first. It is the front controller, which means one central component coordinates web requests. It does not contain the application’s business logic. It receives the request, finds the correct handler, coordinates the processing steps, and sends the final HTTP response back to the client.

2. Find the controller with request mapping

DispatcherServlet uses Handler Mapping to find the correct controller method. Request mapping connects a URL and HTTP method to Java code. Common annotations include @RequestMapping and @GetMapping. In the diagram, the example controller has @RequestMapping("/users") and @GetMapping("/{id}"). Together, they match a request such as GET /users/123. Handler Mapping identifies the controller method that should handle that request.

3. Resolve method arguments and validate input

Spring MVC resolves request data into Java method arguments before normal controller processing continues. The diagram includes path values, query parameters, headers, request bodies, and cookies. Common annotations include @PathVariable, @RequestParam, and @RequestBody. The example uses @PathVariable Long id and an optional @RequestParam named includePosts. Spring can also run Bean Validation. Annotations such as @Valid and @NotNull help check incoming values before the application uses them.

4. Run the controller method

The controller contains the Java method that handles the request. It may perform business logic or call another application service. In the example, getUser calls userService.findUser(id, includePosts). The controller then returns data or a view. A traditional Spring MVC controller can return a model and view for HTML. A REST controller normally returns data that becomes the HTTP response body.

5. Convert the result into the response body

For response bodies, Spring MVC uses HTTP message converters. These components convert Java objects into formats such as JSON, XML, or plain text. The diagram’s example produces JSON with fields such as id, name, and email. @ResponseBody tells Spring to write a returned value into the HTTP response body. @RestController provides response-body behavior for its controller methods. For an HTML response, the controller can instead return a model and view.

6. Handle exceptions and return the response

If processing fails, Spring MVC supports centralized exception handling. The diagram shows @ControllerAdvice handling exceptions and creating proper responses. This keeps common error handling outside individual controller methods. On the normal path, Spring converts the result when needed. DispatcherServlet then sends the HTTP response back to the client. The client can receive HTML or a response body such as JSON.

7. Distinguish Spring MVC from Spring WebFlux

The main difference is the execution model. Spring MVC uses the Servlet API and a blocking, thread-based model. The diagram shows a Servlet-and-threads stack with servers such as Tomcat, Jetty, and Undertow. Spring WebFlux is non-blocking and reactive. It commonly uses Reactor types such as Mono<T> and Flux<T> with an event-loop model, and the diagram shows Netty as its default server. MVC is a strong fit for traditional applications and blocking database calls. WebFlux is useful when high concurrency, streaming, or real-time work is the main requirement.

Practical Complexity & Trade-offs

The benefit of Spring MVC is that its request flow is easy to understand. DispatcherServlet gives the application one central entry point. Handler Mapping connects requests to controller methods. Automatic method argument binding and validation reduce repeated code. HTTP message converters make Java-to-JSON or Java-to-XML responses simple. Centralized exception handling also keeps error handling consistent. The downside is the blocking thread model. A request can keep a thread busy while waiting for database or network work. This works well for many traditional applications, but very high concurrency may need more threads and resources. Spring WebFlux uses non-blocking processing and can handle many waiting operations with fewer threads. However, reactive code is more complex. We accept Spring MVC when simplicity, familiar programming, and compatibility with blocking libraries matter more than reactive scalability.

Why Interviewers Ask This

Interviewers ask this question to check whether you understand the complete HTTP request path, not just Spring annotations. They want to see whether you can explain DispatcherServlet, request mapping, controllers, method arguments, validation, response conversion, and exception handling in the correct order. They also test whether you understand the engineering trade-off between Spring MVC’s blocking Servlet model and Spring WebFlux’s non-blocking reactive model.

Interviewer may ask next
What would you change if this application needed very high concurrency or many streaming requests?

I would first check whether the workload is still a good fit for Spring MVC. The existing MVC flow can remain unchanged for normal blocking requests: the client sends a request, DispatcherServlet coordinates it, Handler Mapping finds the controller, and the result returns through the normal response path. If the main workload becomes high-concurrency streaming or real-time communication, I would consider Spring WebFlux for that workload. WebFlux changes the programming model from blocking Servlet processing to non-blocking reactive processing. Its controller methods commonly return Mono<T> or Flux<T>, and the diagram shows Reactor with an event-loop model and Netty as the default server. Request mapping, validation, controller logic, response conversion, and exception handling still need clear responsibilities. The main downside is complexity. Reactive code uses a different programming style, and blocking database or network calls can remove much of its benefit. I would choose WebFlux only when the concurrency or streaming requirement justifies that added complexity.

How would validation and exception handling work for the GET /users/123 controller flow?

I would keep validation before the controller’s normal business processing and use centralized exception handling for failures. The request first reaches DispatcherServlet. Handler Mapping then selects the controller method for GET /users/123. Spring resolves request values into Java method arguments, including the {id} path value and the optional includePosts query parameter shown in the example. When validation rules such as @Valid or @NotNull apply, Spring checks the input before normal processing continues. If the values are valid, the controller can call userService.findUser(id, includePosts) and return its result. Spring then uses an HTTP message converter when it needs to write a response body such as JSON. If processing throws an exception, the diagram shows @ControllerAdvice handling it centrally and creating a proper response. This keeps repeated error-handling code out of individual controllers. The downside is that centralized handlers must remain specific enough to avoid hiding the real source of an error.

85. What are JPA and Hibernate, and how are they different?NEWSql / DatabaseEasy

Question Details

Define Jakarta Persistence, commonly called JPA, as the Java persistence and object-relational-mapping specification. Define Hibernate ORM as a widely used implementation of that specification with additional features. Explain entities, mappings, persistence context, EntityManager, transactions, lazy loading, generated SQL, and why developers still need SQL and database knowledge.

Short Interview Answer (30-60 seconds)

Jakarta Persistence, commonly called JPA, is the Java specification for object-relational persistence. Hibernate ORM is a popular implementation of that specification. JPA defines standard APIs and behavior, while Hibernate performs the actual ORM work and also provides additional Hibernate-specific features.

Detailed Explanation

JPA and Hibernate help a Java program save, find, change, and remove information in a database without making the developer write every database instruction by hand. One provides a common set of rules that Java programs can follow. The other is a tool that follows those rules and performs the real work. Developers describe how Java objects match stored information, and the tool handles many routine operations. This makes development easier, but developers still need to understand what work is happening behind the scenes so they can prevent slow, incorrect, or wasteful behavior.

Useful Questions to Ask the Interviewer
  1. Would you like only the basic difference between JPA and Hibernate, or should I also explain persistence context, transactions, lazy loading, and generated SQL?
  2. Would you like me to mention Hibernate-specific features beyond the Jakarta Persistence standard?
What are JPA and Hibernate, and how are they different? diagram
How to Explain It in an Interview

Jakarta Persistence, historically known as JPA, is a specification. A specification defines standard interfaces, annotations, and expected behavior for persisting Java objects in relational databases. It is not itself the complete persistence engine.

Hibernate ORM is a widely used ORM implementation that supports Jakarta Persistence. ORM means object-relational mapping. It maps Java objects and fields to relational database tables and columns. Hibernate also provides features beyond the standard Jakarta Persistence API.

An entity is a Java class whose state can be persisted in a database. It is normally marked with @Entity. Mapping annotations such as @Table, @Id, @Column, @OneToMany, and @ManyToOne describe how Java classes, fields, identifiers, and relationships correspond to database structures.

A persistence context is a set of entity instances managed as one unit by the persistence provider. Within one persistence context, one database identity corresponds to one managed entity instance. The provider tracks changes to managed entities and can synchronize those changes with the database.

EntityManager is the main Jakarta Persistence interface used to interact with a persistence context. It can make new entities persistent, find entities by identifier, remove entities, and execute queries. In framework-managed applications, such as many Spring applications, the framework commonly creates and manages the EntityManager and its lifecycle. In a standalone Jakarta Persistence application, an EntityManager can be created from an EntityManagerFactory.

Database modifications normally happen inside a transaction. A transaction defines a unit of work that either commits successfully or is rolled back. Calling persist() does not necessarily execute an INSERT immediately. The provider may delay SQL until the persistence context is flushed. A flush synchronizes pending in-memory entity changes with the database, but flush is not the same as commit. The transaction can still be rolled back after a flush if the transaction has not committed.

Hibernate generates SQL for many entity operations and ORM queries. This reduces repetitive persistence code, but generated SQL still matters. A poor mapping or query can create unnecessary joins, retrieve too much data, or execute too many statements.

Lazy loading means related data can be deferred until the application first accesses it rather than always loading it with the original entity. This can avoid unnecessary database work, but it has tradeoffs. Accessing an unloaded lazy association after the required persistence context is unavailable can fail. Repeatedly accessing lazy associations can also create the N+1 query problem.

The N+1 problem commonly occurs when one query loads a collection of parent entities and then additional queries load related data separately for many or all of those parents. The exact number of extra queries depends on the mapping, fetch strategy, access pattern, and provider optimizations, so developers should inspect the actual SQL instead of assuming a fixed query count.

The main difference is therefore simple: Jakarta Persistence defines the portable persistence contract, while Hibernate ORM is software that implements that contract and adds its own capabilities. Using standard Jakarta Persistence interfaces can reduce direct dependence on Hibernate-specific APIs, although an application can deliberately use Hibernate-specific features when they provide a useful benefit.

Developers still need SQL and database knowledge when using Hibernate. They should understand joins, indexes, constraints, transactions, locking, query plans, and the SQL generated by the ORM. An ORM makes database access more convenient, but it does not automatically make database access efficient or correct.

Technical Approach
  1. State the core distinction: Jakarta Persistence/JPA is a specification, while Hibernate ORM is an implementation.
  2. Explain entities and mappings.
  3. Explain EntityManager and the persistence context.
  4. Explain transaction boundaries, flushing, and commit.
  5. Explain generated SQL and lazy loading.
  6. Mention the N+1 query risk.
  7. Finish by explaining why SQL, indexes, constraints, transactions, and query-plan knowledge are still necessary.
Practical Insights

There is no single time or memory complexity for JPA or Hibernate because the real cost depends on the SQL generated, the amount of data loaded, the mappings, and the database execution plan. ORM also uses application memory to keep managed entities and track changes. Lazy loading can avoid loading unused data, but careless access can cause many extra queries. A large persistence context can use significant memory and make change tracking more expensive. Maintenance is often easier because standard mappings and APIs reduce repetitive persistence code, but developers still need to monitor generated SQL, transaction boundaries, indexes, and query plans.

Why Interviewers Ask This

Interviewers want to know whether the candidate understands the difference between a Java persistence specification and an implementation. They also want to see whether the candidate understands entities, mappings, persistence contexts, EntityManager, transactions, lazy loading, generated SQL, and why using an ORM does not remove the need for SQL and database knowledge.

Common interview mistakes

A common mistake is saying JPA and Hibernate are the same thing. Jakarta Persistence/JPA is the standard contract, while Hibernate ORM is an implementation with additional features. Another mistake is assuming persist() always executes SQL immediately. Candidates may also confuse flush with commit, assume lazy loading is always faster, overlook the N+1 query problem, or believe an ORM removes the need to understand SQL, indexes, constraints, transactions, locking, and query plans.

Interview tip

Start with one clear sentence: JPA is the specification, and Hibernate is an implementation. Then briefly explain entities, EntityManager, persistence context, transactions, generated SQL, and lazy loading. Finish by saying that strong Hibernate developers still inspect SQL and understand database fundamentals.

Interviewer may ask next
What is the persistence context in JPA?

A persistence context is the set of entity instances currently managed by an EntityManager. The provider tracks changes to managed entities and synchronizes those changes with the database when a flush occurs. Within one persistence context, a particular persistent identity corresponds to one managed entity instance. This management enables behavior such as automatic change detection and first-level identity management.

What is the N+1 query problem in Hibernate, and how can you avoid it?

The N+1 problem occurs when an initial query loads a collection of entities and later access to related data causes additional queries for many individual entities. It can often be reduced by choosing an appropriate fetch strategy, such as a fetch join, an entity graph, suitable batch fetching, or a query or DTO projection designed for the required data. The best choice depends on what related data the use case actually needs, so developers should inspect the generated SQL and database execution behavior.

86. What is the difference between WHERE and HAVING?Sql / DatabaseMedium

Question Details

Explain where WHERE and HAVING are used in SQL and how they affect filtering and grouping.

Short Interview Answer (30-60 seconds)

WHERE filters individual rows before GROUP BY and aggregation. HAVING filters groups after aggregation. Use WHERE for source-row conditions, such as status = 'PAID', and HAVING for group conditions, such as SUM(amount) > 1000.

Detailed Explanation

See the Code while reading this explanation.

This question asks when SQL should remove individual records and when it should remove completed summaries. The first choice checks each original row before rows are combined or totals are calculated. The second choice checks the finished groups, such as departments with more than five employees. This difference matters because filtering earlier changes which rows are included in each total, while filtering later decides which completed totals are returned. Choosing the correct clause keeps the result accurate, makes the query easier to understand, and can avoid processing rows that are not needed.

Useful clarifying questions:

Useful Questions to Ask the Interviewer
  • Are we filtering individual rows, grouped results, or both?
  • Does the condition depend on an aggregate value such as COUNT, SUM, or AVG?
What is the difference between WHERE and HAVING? diagram
How to Explain It in an Interview

WHERE filters source rows before GROUP BY and aggregate calculations are logically performed. It is used for conditions that can be evaluated for each individual row, such as status = 'PAID', country = 'US', or order_date >= DATE '2026-01-01'.

HAVING filters the groups produced by grouping and aggregation. It is commonly used when the condition depends on an aggregate result, such as COUNT(*) >= 5, SUM(amount) > 1000, or AVG(score) >= 80.

For example, suppose the requirement is to find customers whose paid orders during 2026 total more than 1,000. WHERE first keeps only paid orders from 2026. SQL then groups those remaining rows by customer and calculates each customer's total. HAVING removes customer groups whose calculated total is not greater than 1,000.

The practical rule is:

  • Use WHERE for conditions on individual input rows.
  • Use HAVING for conditions on grouped or aggregated results.
  • Use both when the input rows and the completed groups require separate filtering.

A HAVING condition does not always need to contain an aggregate function. It may also reference a grouping column, depending on the database and query. However, when an equivalent condition can safely be applied to each source row, placing it in WHERE is usually clearer and may reduce the rows that must be grouped.

Some database systems also allow HAVING without an explicit GROUP BY. In that case, the qualifying input is generally treated as one group. This behavior is supported differently across database products, so it should not be relied on when portability is important.

SQL is declarative, so the database optimizer may rewrite predicates or push safe conditions earlier. The logical distinction still remains: WHERE determines which rows participate in grouping and aggregation, while HAVING determines which resulting groups are returned.

Technical Approach
  1. Identify each filtering condition in the requirement.
  2. Decide whether the condition applies to an individual source row or to a completed group.
  3. Put row-level conditions in WHERE.
  4. Apply GROUP BY when grouped aggregation is required.
  5. Calculate aggregate values such as COUNT, SUM, or AVG.
  6. Put aggregate or group-result conditions in HAVING.
  7. Verify that moving a condition would not change which rows contribute to the aggregates.
Practical Insights

The exact execution cost depends on the database, indexes, statistics, data distribution, grouping columns, and query plan. A selective WHERE condition can reduce the number of rows read, grouped, sorted, hashed, or aggregated. This may lower CPU use, memory pressure, temporary-disk use, and execution time. HAVING normally filters after group results are formed logically, although an optimizer may push safe predicates earlier. No fixed time or memory complexity can be stated without the schema and execution plan.

Code
SELECT
  customer_id,
  SUM(amount) AS total_paid_amount
FROM
  orders
WHERE
  status = 'PAID'
  AND order_date >= DATE '2026-01-01'
  AND order_date < DATE '2027-01-01'
GROUP BY
  customer_id
HAVING
  SUM(amount) > 1000
ORDER BY
  total_paid_amount DESC;
Why Interviewers Ask This

Interviewers ask this question to verify that the candidate understands row-level filtering, group-level filtering, aggregate functions, SQL logical processing order, query correctness, and the performance benefit of filtering unnecessary rows before grouping when possible.

Common interview mistakes

Common mistakes include writing an aggregate condition at the same query level in WHERE, such as WHERE COUNT(*) > 5; treating WHERE and HAVING as interchangeable; putting every condition in HAVING even when it applies to source rows; forgetting that WHERE changes which rows contribute to an aggregate; and selecting nonaggregated columns that are not included in GROUP BY. Another mistake is assuming that an optimizer will always move a row-level HAVING condition into WHERE. Such rewrites are database-specific and are not guaranteed.

Interview tip

Start with one sentence: WHERE filters rows before grouping, while HAVING filters groups after aggregation. Then show a query that uses both clauses and explain how WHERE changes the aggregate input while HAVING filters the completed aggregate results.

Interviewer may ask next
Can WHERE and HAVING be used in the same SQL query?

Yes. WHERE filters the source rows first, GROUP BY forms groups from the remaining rows, and HAVING filters those group results. For example, WHERE can keep only paid orders, while HAVING returns only customers whose paid-order total exceeds a threshold.

Can HAVING contain a condition that does not use an aggregate function?

Yes, especially when the condition references a grouping column, although exact rules vary by database. When the same condition can safely be applied to individual source rows, WHERE is usually clearer and may reduce the work required for grouping. It must not be moved when doing so would change which rows contribute to the aggregate.

87. Explain INNER JOIN, LEFT JOIN, RIGHT JOIN, and FULL OUTER JOIN.Sql / DatabaseMedium

Question Details

Compare the main SQL join types and describe when each one is appropriate.

Short Interview Answer (30-60 seconds)

INNER JOIN returns only matching rows. LEFT JOIN keeps all left-side rows, RIGHT JOIN keeps all right-side rows, and FULL OUTER JOIN keeps all rows from both sides. In an outer join, columns belonging to a missing match contain NULL.

Detailed Explanation

See the Code while reading this explanation.

This question asks how to combine information from two lists when some entries have a partner and others do not. The important decision is which unpaired entries must remain in the final result. You may want only paired entries, every entry from the first list, every entry from the second list, or every entry from both lists. When an entry has no partner but must still be shown, the missing information appears as empty. Choosing the wrong method can silently remove important records or produce unexpected extra rows.

Useful clarifying questions:

Useful Questions to Ask the Interviewer
  • Which table must keep all of its rows?
  • Should unmatched rows from either table appear?
  • Are the join columns unique, nullable, or duplicated?
  • Which database product must run the query?
Explain INNER JOIN, LEFT JOIN, RIGHT JOIN, and FULL OUTER JOIN. diagram
How to Explain It in an Interview

A SQL join combines rows from two table expressions by evaluating a join condition, commonly an equality such as customers.customer_id = orders.customer_id.

INNER JOIN returns only row combinations for which the join condition evaluates to true. Rows without a match are excluded from the result. Use it when the result should include only records that have related data on both sides, such as orders that reference an existing customer.

LEFT JOIN, also called LEFT OUTER JOIN, returns every row from the left table expression and any matching rows from the right table expression. When no right-side match exists, the selected right-side columns are returned as NULL. Use it when the left side is the complete population that must be preserved, such as all customers including customers with no orders.

RIGHT JOIN, also called RIGHT OUTER JOIN, returns every row from the right table expression and any matching rows from the left table expression. When no left-side match exists, the selected left-side columns are returned as NULL. It is logically equivalent to swapping the table positions and using a LEFT JOIN. Many teams prefer LEFT JOIN for consistency and readability, but RIGHT JOIN is valid where the database supports it.

FULL OUTER JOIN returns matching row combinations plus unmatched rows from both sides. For an unmatched row, columns from the missing side are returned as NULL. Use it for reconciliation, migration validation, dataset comparison, and finding records present in one source but absent from the other. PostgreSQL, SQL Server, and Oracle support FULL OUTER JOIN. MySQL does not provide a native FULL OUTER JOIN operator, so a carefully designed alternative such as combining complementary outer joins with UNION ALL may be required.

The words left and right refer only to the table-expression positions in the SQL statement. They do not describe business priority.

A join does not necessarily return one result row for each source row. It returns every row combination that satisfies the join condition. If a key occurs three times on one side and twice on the other, that key can produce six matching result rows.

NULL also requires care. A comparison such as column = NULL is not true; use IS NULL to test for missing values. In addition, placing a condition on the non-preserved side in the WHERE clause can remove NULL-extended rows and make an outer join behave like an inner join. Put that condition in the ON clause when it should restrict matches without removing preserved rows.

Technical Approach
  1. Identify the two table expressions and the columns that define a valid relationship.
  2. Decide whether unmatched rows should be excluded or preserved.
  3. Use INNER JOIN when only matches are required.
  4. Use LEFT JOIN when all left-side rows must remain.
  5. Use RIGHT JOIN when all right-side rows must remain, or swap the table order and use LEFT JOIN.
  6. Use FULL OUTER JOIN when unmatched rows from both sides must remain.
  7. Check whether duplicate join values can multiply result rows.
  8. Place predicates in ON or WHERE according to whether they should restrict matching or remove final rows.
  9. Verify database support and inspect the execution plan when performance matters.
Practical Insights

A join has no single fixed time or memory cost because the database chooses an execution strategy. A nested-loop join may repeatedly search one input for rows from the other input. A hash join may build an in-memory hash table and can spill temporary data to disk when memory is insufficient. A merge join usually benefits from inputs that are already ordered or can be sorted. Table sizes, matching-row counts, indexes, data distribution, statistics, available memory, and the selected columns all affect cost. An index on a useful join column can reduce lookup work, but indexes consume storage and add work to inserts, updates, and deletes. Duplicate join values can greatly increase result size, network transfer, application memory use, and processing time.

Code
CREATE TABLE customers (
  customer_id INTEGER PRIMARY KEY,
  customer_name VARCHAR(100) NOT NULL
);


CREATE TABLE orders (
  order_id INTEGER PRIMARY KEY,
  customer_id INTEGER,
  order_total DECIMAL(10, 2) NOT NULL
);


INSERT INTO
  customers (customer_id, customer_name)
VALUES
  (1, 'Asha'),
  (2, 'Ben'),
  (3, 'Chen');


INSERT INTO
  orders (order_id, customer_id, order_total)
VALUES
  (101, 1, 50.00),
  (102, 1, 75.00),
  (103, 4, 20.00);


SELECT
  c.customer_id,
  c.customer_name,
  o.order_id,
  o.order_total
FROM
  customers AS c
  INNER JOIN orders AS o ON o.customer_id = c.customer_id;


SELECT
  c.customer_id,
  c.customer_name,
  o.order_id,
  o.order_total
FROM
  customers AS c
  LEFT JOIN orders AS o ON o.customer_id = c.customer_id;


SELECT
  c.customer_id,
  c.customer_name,
  o.order_id,
  o.order_total
FROM
  customers AS c
  RIGHT JOIN orders AS o ON o.customer_id = c.customer_id;


SELECT
  c.customer_id,
  c.customer_name,
  o.order_id,
  o.order_total
FROM
  customers AS c
  FULL OUTER JOIN orders AS o ON o.customer_id = c.customer_id;
Why Interviewers Ask This

Interviewers ask this question to verify that the candidate can select the correct join type, predict which matched and unmatched rows will appear, understand how table order affects outer joins, recognize row multiplication caused by duplicate keys, and avoid filters that accidentally change outer-join behavior.

Common interview mistakes

Common mistakes include reversing which side a LEFT or RIGHT JOIN preserves, forgetting that table order defines left and right, and assuming that each source row produces at most one output row. Duplicate join values can multiply matches. Another frequent mistake is putting a condition on the non-preserved table in the WHERE clause after an outer join, which removes NULL-extended rows and may effectively turn it into an INNER JOIN. Other mistakes include using = NULL instead of IS NULL, joining on incomplete or incorrect keys, using DISTINCT to hide an incorrect join, and assuming every database supports RIGHT JOIN or FULL OUTER JOIN.

Interview tip

Start with one decision: which unmatched rows must remain? Explain all four joins with the same two-table example, mention NULL for missing matches, and then warn about duplicate-key row multiplication and WHERE-clause filters on outer joins.

Interviewer may ask next
How can a WHERE clause accidentally change the behavior of a LEFT JOIN?

A LEFT JOIN creates NULL values for right-side columns when a left row has no match. A WHERE condition such as WHERE o.status = 'PAID' rejects those NULL-extended rows, so unmatched left rows disappear and the result may behave like an INNER JOIN. Put the predicate in the ON clause when it should limit which right rows match while still preserving every left row.

How can FULL OUTER JOIN be represented in a database that does not support it natively?

One common pattern combines a LEFT JOIN with a second query that returns only unmatched rows from the opposite side, using UNION ALL. The second query must filter for a reliably non-nullable key from the first table with IS NULL; otherwise matched rows may be duplicated or valid rows may be misclassified. The exact query should be tested against duplicates, nullable keys, and the target database's behavior.

88. What is a correlated subquery?Sql / DatabaseMedium

Question Details

Explain correlated subqueries and how they differ from ordinary subqueries.

Short Interview Answer (30-60 seconds)

A correlated subquery is an inner query that uses a value from the current row of the outer query. Unlike an ordinary subquery, it cannot normally run independently. It is commonly used with EXISTS, NOT EXISTS, or row-specific comparisons.

Detailed Explanation

See the Code while reading this explanation.

This question asks about a smaller search placed inside a larger search. The key idea is that the smaller search uses information from the current item being checked by the larger search. Its result can therefore be different for each item. A normal smaller search does not depend on the larger one and can usually be checked separately. The interviewer wants to know whether you can explain this difference, recognize useful cases, identify possible errors, and understand that the database may reorganize the work internally to make it more efficient.

Useful clarifying questions:

Useful Questions to Ask the Interviewer
  • Would you like an EXISTS example or a comparison example?
  • Should I compare it with a join or a window function?
  • Are we discussing logical SQL behavior or a particular database execution plan?
What is a correlated subquery? diagram
How to Explain It in an Interview

A correlated subquery is a subquery that references one or more columns from an outer query. The outer query provides values from its current candidate row, and the inner query uses those values when evaluating related data.

For example, suppose each employee belongs to a department. To find employees whose salary is above their own department's average, the inner query can calculate the average salary for the department of the current outer employee. The reference e.department_id connects the inner query to the outer row.

An ordinary, or uncorrelated, subquery does not reference the outer query. It can normally be evaluated independently. For example, a subquery that calculates the average salary for the entire company has the same result regardless of which outer employee is being considered.

The logical model is that the correlated condition is evaluated in the context of each candidate outer row. However, this does not mean that the database must physically rerun the inner query once for every row. The optimizer may transform the query into a join, semi-join, anti-join, grouped operation, or another equivalent plan.

Correlated subqueries are especially natural with EXISTS and NOT EXISTS. EXISTS returns true when the inner query can produce at least one matching row. The values in its select list are not used, so SELECT 1 is commonly written to show that only existence matters. NOT EXISTS returns true when no matching row exists and is often safer than NOT IN when the compared inner column may contain NULL.

A correlated scalar subquery used with operators such as =, >, or < must return at most one value for each outer row. If it returns multiple rows, most databases raise an error. An aggregate such as AVG normally produces one row, but it can return NULL when no qualifying non-NULL values exist. A comparison with that NULL result is unknown and does not pass a WHERE condition unless the query handles the case explicitly.

Use a correlated subquery when it expresses a row-dependent rule clearly. Consider a join, grouped derived table, common table expression, or window function when it is easier to understand, avoids repeated logical work, or produces a better plan. Performance depends on the database engine, indexes, statistics, data distribution, and optimizer decisions. Important production queries should be tested with representative data and inspected using the actual execution plan.

Technical Approach
  1. Identify the outer query and the candidate rows it produces.
  2. Inspect the inner query for references to an alias defined by the outer query.
  3. Explain that this outer reference makes the inner result depend on the current outer row.
  4. Contrast it with an ordinary subquery that has no outer reference and can normally run independently.
  5. Determine whether the subquery is used for existence checking, a scalar comparison, or another row-dependent rule.
  6. Check scalar-subquery cardinality and NULL behavior where applicable.
  7. Explain that the optimizer may rewrite the physical execution while preserving the same logical result.
  8. For production use, compare suitable alternatives and inspect the actual execution plan with representative data.
Practical Insights

A correlated subquery has no single fixed cost. In a simple mental model, the database may need to check related inner data for every outer row. If there are N outer rows and an unindexed inner scan examines M rows each time, the work can approach N multiplied by M. A suitable index, early EXISTS termination, cached work, or an optimizer rewrite can reduce that cost substantially. Memory use depends on the chosen physical plan. A nested-loop plan may use little extra memory, while a rewritten hash join, sort, or aggregation may require memory proportional to an intermediate result and may spill to temporary storage. Deeply nested correlated logic can also increase maintenance and testing cost.

Code
-- Correlated subquery: compare each employee with the average
-- salary of that employee's department.
SELECT
  e.employee_id,
  e.employee_name,
  e.department_id,
  e.salary
FROM
  employee AS e
WHERE
  e.salary > (
    SELECT
      AVG(d.salary)
    FROM
      employee AS d
    WHERE
      d.department_id = e.department_id
  );


-- Ordinary subquery: the inner query is independent of the outer query.
SELECT
  e.employee_id,
  e.employee_name,
  e.salary
FROM
  employee AS e
WHERE
  e.salary > (
    SELECT
      AVG(salary)
    FROM
      employee
  );


-- Correlated EXISTS example.
SELECT
  e.employee_id,
  e.employee_name
FROM
  employee AS e
WHERE
  EXISTS (
    SELECT
      1
    FROM
      assignment AS a
    WHERE
      a.employee_id = e.employee_id
      AND a.status = 'APPROVED'
  );
Why Interviewers Ask This

Interviewers ask this question to test whether the candidate understands nested-query scope, outer-column references, and the difference between correlated and uncorrelated subqueries. It also evaluates whether the candidate can choose among EXISTS, NOT EXISTS, joins, aggregation, and window functions; recognize scalar-subquery and NULL-related edge cases; and discuss performance without incorrectly assuming that every correlated subquery is physically executed once for each outer row.

Common interview mistakes

Common mistakes include calling every nested query correlated; failing to identify the outer-column reference; claiming that the database always executes the inner query once per outer row; using a scalar correlated subquery that can return multiple rows; ignoring the effect of NULL from an empty aggregate result; using NOT IN without considering NULL values; selecting unnecessary columns inside EXISTS; writing ambiguous unqualified column names; assuming that correlated syntax is always slower than a join; and making performance claims without checking indexes, statistics, representative data, and the actual execution plan.

Interview tip

Begin with the dependency: the inner query references the current outer row. Contrast it with an independent subquery, show one concise SQL example, mention scalar and NULL edge cases, and explain that the optimizer may choose a different but equivalent physical plan.

Interviewer may ask next
When should EXISTS be preferred over a join in a correlated query?

Use EXISTS when the requirement is only to determine whether at least one related row exists. It expresses that intention directly and does not duplicate an outer row when multiple inner rows match. A join is appropriate when columns from both tables are required, but it may need grouping or DISTINCT if only one outer row should be returned. The optimizer may produce similar physical plans, so choose the clearest correct form and verify important cases with the actual execution plan.

Are correlated subqueries always slower than ordinary subqueries or joins?

No. Performance depends on the database optimizer, query structure, indexes, statistics, data distribution, and result size. A database may transform a correlated EXISTS into a semi-join or rewrite other correlated logic into an efficient plan. A correlated query can still be expensive when it causes repeated scans or prevents an effective transformation. Compare suitable alternatives using representative data and inspect the actual execution plan instead of assuming one syntax is always faster.

89. How does GROUP BY work with aggregate functions?Sql / DatabaseMedium

Question Details

Explain grouping and aggregation in SQL and how they are used together.

Short Interview Answer (30-60 seconds)

GROUP BY places rows with matching grouping values into the same group. Aggregate functions such as COUNT, SUM, AVG, MIN, and MAX then calculate one value for each group. WHERE filters rows before grouping, while HAVING filters groups after aggregation.

Detailed Explanation

See the Code while reading this explanation.

This question asks how many records can be arranged into matching sets and then summarized. For example, a company may want one result for each customer instead of one result for every order. The answer should explain what decides which records belong together, what summary is calculated for each set, and why the result usually contains fewer lines. It should also explain the difference between removing individual records before the sets are created and removing completed sets after their summary values have been calculated.

Useful clarifying questions:

Useful Questions to Ask the Interviewer
  • Should I cover grouping by multiple columns?
  • Should I explain WHERE versus HAVING?
  • Should I include NULL behavior and performance considerations?
How does GROUP BY work with aggregate functions? diagram
How to Explain It in an Interview

GROUP BY organizes the rows that remain after FROM, joins, and WHERE filtering into groups. Rows belong to the same group when their grouping expressions have equal grouping values. Aggregate functions then calculate one result for each group.

For example, grouping orders by customer_id creates one group for each customer. COUNT(*) counts the rows in each customer's group, and SUM(amount) adds the non-NULL amounts in that group. The result normally contains one row per customer instead of one row per order.

A useful conceptual processing order is:

  1. FROM and joins produce the input rows.
  2. WHERE removes individual rows.
  3. GROUP BY forms groups from the remaining rows.
  4. Aggregate functions calculate values for each group.
  5. HAVING removes groups that do not satisfy its condition.
  6. SELECT produces the requested result expressions.
  7. ORDER BY sorts the final rows.

The exact physical execution chosen by the database may differ from this conceptual order as long as the result remains equivalent.

In a grouped query, each selected expression must be valid for one result row per group. Portable SQL should select only:

  • Expressions included in the GROUP BY clause.
  • Aggregate expressions such as COUNT(*) or SUM(amount).
  • Expressions derived only from grouped values or aggregate results.

Some databases permit a non-grouped column when they can prove that it is functionally dependent on the grouped columns. Other databases or SQL modes apply different rules. For portable and predictable SQL, explicitly group every selected non-aggregate value required to identify the group.

Common aggregate functions are:

  • COUNT(*): counts every row in the group.
  • COUNT(column): counts rows where that expression is not NULL.
  • SUM(column): adds the non-NULL numeric values.
  • AVG(column): averages the non-NULL numeric values.
  • MIN(column) and MAX(column): return the smallest and largest non-NULL values.

Without GROUP BY, an aggregate query treats all qualifying rows as one implicit group. It usually returns one summary row. If no rows qualify, COUNT(*) returns zero, while aggregates such as SUM, AVG, MIN, and MAX normally return NULL. With an explicit GROUP BY, no qualifying input rows normally produce no groups and therefore no result rows.

When several columns appear in GROUP BY, SQL creates one group for each distinct combination of their grouping values. For example, grouping by customer_id and status produces a separate group for each customer-and-status combination. Adding an unnecessary grouping column changes the result granularity and may create many more rows.

WHERE and HAVING perform different jobs. WHERE filters individual rows before groups and aggregate results are created. HAVING filters groups after aggregation. For example, WHERE status = 'COMPLETED' removes individual orders, while HAVING SUM(amount) > 1000 keeps only customer groups whose total exceeds 1000. A non-aggregate condition should normally be placed in WHERE when it is logically equivalent, because early filtering can reduce the amount of data processed.

For grouping, all NULL values of the same grouping expression are placed into one group. Most aggregate functions ignore NULL inputs, but COUNT(*) counts the row itself. Therefore, COUNT(*) and COUNT(column) can return different values. AVG(column) divides the sum of non-NULL values by the count of non-NULL values, not by the total number of rows.

A condition in HAVING should be written using valid grouped or aggregate expressions according to the database's rules. Column aliases in GROUP BY or HAVING are database-specific, so repeating the expression or using a subquery or common table expression is safer when portability matters.

For large queries, performance depends on the number of qualifying rows, the number and width of distinct grouping keys, joins, available memory, indexes, and the database's execution plan. A database commonly uses hash aggregation, sort-based aggregation, or an index-supported ordered strategy. Hash aggregation generally needs memory related to the number and size of groups. Sort-based aggregation must order the relevant rows and may spill temporary data to disk when memory is insufficient. An index may help filtering or provide a useful row order, but it does not guarantee that grouping will avoid scanning, sorting, hashing, or temporary storage. Production performance should be verified with the database's actual execution plan and representative data.

Technical Approach
  1. Identify the input table or joined rows.
  2. Put row-level filters in WHERE.
  3. Choose the expression or expressions that define one group.
  4. Select only grouped values, aggregate results, or valid expressions derived from them.
  5. Apply COUNT, SUM, AVG, MIN, MAX, or another required aggregate.
  6. Put aggregate-based filters in HAVING.
  7. Use ORDER BY only when a defined output order is required.
  8. Test NULL and empty-input behavior.
  9. For large data sets, inspect the actual execution plan and measure the query with representative data.
Practical Insights

The database must examine the qualifying rows and organize them into groups. A hash-based plan is commonly close to one grouping operation per input row, but it needs memory for the distinct groups and their running aggregate values. A sort-based plan must order rows by the grouping keys, which is generally more work as the input grows and may require temporary disk space. Wide grouping keys, many distinct groups, large joins, and insufficient memory increase cost. Early WHERE filtering can reduce the input. Indexes can help filtering or ordering, but they consume storage and add maintenance work to inserts, updates, and deletes.

Code
SELECT
  customer_id,
  COUNT(*) AS order_count,
  SUM(amount) AS total_amount
FROM
  orders
WHERE
  status = 'COMPLETED'
  AND created_at >= DATE '2026-01-01'
GROUP BY
  customer_id
HAVING
  SUM(amount) > 1000
ORDER BY
  total_amount DESC;
Why Interviewers Ask This

Interviewers ask this question to verify that the candidate understands how SQL summarizes multiple rows, how grouping columns determine the result granularity, how WHERE differs from HAVING, how NULL values affect aggregates, and how to write grouped queries without selecting ambiguous non-aggregate values.

Common interview mistakes

Common mistakes include selecting a non-aggregate value that does not validly identify the group, placing an aggregate condition in WHERE, using HAVING for a row-level condition that belongs in WHERE, assuming COUNT(column) counts NULL values, forgetting that AVG ignores NULL values, adding unnecessary grouping columns and changing the result granularity, expecting an explicit GROUP BY query with no matching rows to return one row, relying on database-specific alias or functional-dependency behavior, assuming result order without ORDER BY, and claiming that an index automatically removes aggregation cost.

Interview tip

Start with the phrase "one result row per group." Give a small customer-orders example, distinguish WHERE from HAVING, explain COUNT(*) versus COUNT(column), and mention that multiple grouping columns change the result granularity. Finish by saying that large grouped queries should be checked with the actual execution plan.

Interviewer may ask next
What is the difference between WHERE and HAVING in a grouped query?

WHERE filters individual input rows before grouping and aggregate calculation. HAVING filters the groups after aggregate values have been calculated. For example, WHERE status = 'COMPLETED' removes non-completed orders before grouping, while HAVING SUM(amount) > 1000 removes customer groups whose calculated total is not greater than 1000.

How do NULL values and empty input affect grouped aggregate queries?

Rows with NULL in the same grouping expression form one group. COUNT(*) counts every row, while COUNT(column) and most other aggregates ignore NULL inputs. Without GROUP BY, an aggregate query over no qualifying rows usually returns one row with COUNT equal to zero and SUM, AVG, MIN, and MAX equal to NULL. With an explicit GROUP BY, no qualifying rows normally produce no result rows.

90. How would you find the second highest salary in a table?Sql / DatabaseMedium

Question Details

Explain one or more SQL approaches to finding the second highest value and discuss edge cases.

Short Interview Answer (30-60 seconds)

I would first confirm that “second highest” means the second distinct salary. For only the value, I would find the maximum salary below the overall maximum. If employee rows are also needed, I would use DENSE_RANK so tied salaries receive the same rank.

Detailed Explanation

See the Code while reading this explanation.

This question asks you to examine employee pay amounts and return the amount immediately below the largest different amount. The important detail is how repeated amounts should be treated. If several employees receive the highest pay, they normally count as one pay level, and the next lower amount is the answer. You should also consider missing pay values, a table with no rows, and a table with only one different pay amount. A strong answer explains these cases and chooses a method that matches whether only the amount or complete employee details are required.

Useful clarifying questions:

Useful Questions to Ask the Interviewer
  • Does “second highest” mean the second distinct salary?
  • Should I return only the salary or all employees earning it?
  • Which database system should the SQL target?
How would you find the second highest salary in a table? diagram
How to Explain It in an Interview

I would normally interpret the requirement as the second highest distinct, non-NULL salary.

For only the salary value, a portable and clear approach is:

SELECT MAX(salary) AS second_highest_salary FROM employees WHERE salary < ( SELECT MAX(salary) FROM employees );

The inner query finds the highest non-NULL salary. The outer query considers only salaries below that value and returns the largest one. Duplicate highest salaries do not change the result because every value equal to the maximum is excluded. Standard aggregate functions such as MAX ignore NULL values. If the table is empty, all salaries are NULL, or fewer than two distinct non-NULL salaries exist, the result is NULL.

A sorting approach is also possible:

SELECT DISTINCT salary FROM employees WHERE salary IS NOT NULL ORDER BY salary DESC OFFSET 1 ROW FETCH NEXT 1 ROW ONLY;

This sorts the distinct salary values from highest to lowest, skips the first value, and returns the next one. Pagination syntax differs by database. PostgreSQL and MySQL commonly use LIMIT 1 OFFSET 1, while SQL Server can use OFFSET 1 ROWS FETCH NEXT 1 ROWS ONLY with ORDER BY.

If the requirement is to return all employees who earn the second highest distinct salary, use DENSE_RANK:

WITH ranked_salaries AS ( SELECT employee_id, employee_name, salary, DENSE_RANK() OVER (ORDER BY salary DESC) AS salary_rank FROM employees WHERE salary IS NOT NULL ) SELECT employee_id, employee_name, salary FROM ranked_salaries WHERE salary_rank = 2;

DENSE_RANK gives equal salaries the same rank and assigns consecutive ranks to different salary values. Therefore, every employee tied at the second highest distinct salary receives rank 2.

ROW_NUMBER is not appropriate when ties must share a rank because it numbers individual rows. RANK also differs from DENSE_RANK because it leaves gaps after ties. For example, if two employees share the highest salary, RANK assigns the next lower salary rank 3 rather than rank 2.

For a single salary value, the nested MAX query is concise and broadly portable. For employee details, ties, or additional salary levels, DENSE_RANK is usually easier to extend and explain.

Technical Approach

1. Confirm whether duplicate salaries count once; the usual requirement is the second distinct salary. 2. Decide whether the output should contain only the salary value or complete employee rows. 3. Exclude NULL salaries from ranking or comparisons. 4. For only the value, find the overall maximum and then the maximum salary below it. 5. For employee rows, assign DENSE_RANK values in descending salary order and select rank 2. 6. Define the expected result when fewer than two distinct non-NULL salaries exist.

Practical Insights

Without a useful index, the database may scan the salary column for the inner MAX and again for the outer MAX, so the work is proportional to the number of rows, while the query itself needs little additional working memory. A database optimizer may choose a different physical plan. An index whose leading column is salary can help find the largest values with fewer reads, although the exact benefit depends on the database, index structure, table statistics, and execution plan. DISTINCT with ORDER BY and DENSE_RANK may require sorting or an index-ordered scan. A sort can use memory proportional to the rows or distinct values being processed and may spill to temporary disk space when the available memory is insufficient.

Code
-- Return only the second highest distinct salary
SELECT
  MAX(salary) AS second_highest_salary
FROM
  employees
WHERE
  salary < (
    SELECT
      MAX(salary)
    FROM
      employees
  );


-- Return all employees earning the second highest distinct salary
WITH
  ranked_salaries AS (
    SELECT
      employee_id,
      employee_name,
      salary,
      DENSE_RANK() OVER (
        ORDER BY
          salary DESC
      ) AS salary_rank
    FROM
      employees
    WHERE
      salary IS NOT NULL
  )
SELECT
  employee_id,
  employee_name,
  salary
FROM
  ranked_salaries
WHERE
  salary_rank = 2;
Why Interviewers Ask This

This question checks whether the candidate understands aggregation, ordering, duplicate values, NULL handling, ranking functions, and the difference between the second row and the second distinct value. It also tests whether the candidate can choose an appropriate SQL approach, explain database-specific syntax, handle missing results, and discuss performance without making unsupported assumptions.

Common interview mistakes

Common mistakes include returning the second physical row instead of the second distinct salary, forgetting DISTINCT in an OFFSET-based query, using ROW_NUMBER when tied salaries should share a rank, and using RANK while expecting consecutive ranks after ties. Other mistakes include ignoring NULL values, failing to define the result when fewer than two distinct salaries exist, assuming pagination syntax is identical across databases, and returning one arbitrary employee when several employees share the second highest salary.

Interview tip

State the duplicate-salary assumption first. Present the nested MAX query for a single value, then explain that DENSE_RANK is better when employee rows or ties must be returned. Briefly mention NULL handling, insufficient distinct values, and database-specific pagination syntax.

Interviewer may ask next
How would you return every employee who earns the second highest salary?

Use DENSE_RANK over salary in descending order and filter for salary_rank = 2. DENSE_RANK assigns the same rank to equal salaries and gives consecutive ranks to different salary values, so every employee tied at the second highest distinct salary is returned.

What should happen when the table has fewer than two distinct non-NULL salaries?

The nested MAX query returns NULL because no salary exists below the maximum. The DENSE_RANK query filtered to rank 2 returns no rows. The application or API contract should explicitly define whether that database result is exposed as NULL, an empty result, or a domain-specific message.

More questions load as you scroll

Disclaimer: This interview guide is for educational and informational purposes only. It is designed to help readers prepare, but it does not guarantee any interview result, hiring decision, offer, or outcome. Interview questions, hiring criteria, and preferred answers can vary by employer, interviewer, industry, location, and time. The examples and explanations reflect the authors' research and judgment, are provided without warranties of any kind, and should not be treated as the only correct approach. Diagrams are simplified illustrations intended to highlight the main components and their interactions; actual systems and implementations may be more complex. Alternative approaches may be equally valid or better suited to a particular question, context, or interviewer. To the fullest extent permitted by applicable law, the author, contributors, and publisher are not liable for decisions made, actions taken, or losses incurred based on this guide.