Search...

Saturday, April 4, 2015

SQL Interview Questions

SQL Queries Interview Questions - Oracle Part 1
As a database developer, writing SQL queries, PLSQL code is part of daily life. Having a good knowledge on SQL is really important. Here i am posting some practical examples on SQL queries.

To solve these interview questions on SQL queries you have to create the products, sales tables in your oracle database. The "Create Table", "Insert" statements are provided below.




CREATE TABLE PRODUCTS
(
       PRODUCT_ID     INTEGER,
       PRODUCT_NAME   VARCHAR2(30)
);
CREATE TABLE SALES
(
       SALE_ID        INTEGER,
       PRODUCT_ID     INTEGER,
       YEAR           INTEGER,
       Quantity       INTEGER,
       PRICE          INTEGER
);      

INSERT INTO PRODUCTS VALUES ( 100, 'Nokia');
INSERT INTO PRODUCTS VALUES ( 200, 'IPhone');
INSERT INTO PRODUCTS VALUES ( 300, 'Samsung');
INSERT INTO PRODUCTS VALUES ( 400, 'LG');

INSERT INTO SALES VALUES ( 1, 100, 2010, 25, 5000);
INSERT INTO SALES VALUES ( 2, 100, 2011, 16, 5000);
INSERT INTO SALES VALUES ( 3, 100, 2012, 8,  5000);
INSERT INTO SALES VALUES ( 4, 200, 2010, 10, 9000);
INSERT INTO SALES VALUES ( 5, 200, 2011, 15, 9000);
INSERT INTO SALES VALUES ( 6, 200, 2012, 20, 9000);
INSERT INTO SALES VALUES ( 7, 300, 2010, 20, 7000);
INSERT INTO SALES VALUES ( 8, 300, 2011, 18, 7000);
INSERT INTO SALES VALUES ( 9, 300, 2012, 20, 7000);
COMMIT;

The products table contains the below data.


SELECT * FROM PRODUCTS;

PRODUCT_ID PRODUCT_NAME
-----------------------
100        Nokia
200        IPhone
300        Samsung

The sales table contains the following data.


SELECT * FROM SALES;

SALE_ID PRODUCT_ID YEAR QUANTITY PRICE
--------------------------------------
1       100        2010   25     5000
2       100        2011   16     5000
3       100        2012   8      5000
4       200        2010   10     9000
5       200        2011   15     9000
6       200        2012   20     9000
7       300        2010   20     7000
8       300        2011   18     7000
9       300        2012   20     7000

Here Quantity is the number of products sold in each year. Price is the sale price of each product.

I hope you have created the tables in your oracle database. Now try to solve the below SQL queries.

1. Write a SQL query to find the products which have continuous increase in sales every year?

Solution:

Here “Iphone” is the only product whose sales are increasing every year.

STEP1: First we will get the previous year sales for each product. The SQL query to do this is


SELECT P.PRODUCT_NAME,
       S.YEAR,
       S.QUANTITY,
       LEAD(S.QUANTITY,1,0) OVER (
                            PARTITION BY P.PRODUCT_ID
                            ORDER BY S.YEAR DESC
                            ) QUAN_PREV_YEAR
FROM   PRODUCTS P,
       SALES S
WHERE  P.PRODUCT_ID = S.PRODUCT_ID;


PRODUCT_NAME YEAR QUANTITY QUAN_PREV_YEAR
-----------------------------------------
Nokia        2012    8         16
Nokia        2011    16        25
Nokia        2010    25        0
IPhone       2012    20        15
IPhone       2011    15        10
IPhone       2010    10        0
Samsung      2012    20        18
Samsung      2011    18        20
Samsung      2010    20        0

Here the lead analytic function will get the quantity of a product in its previous year.

STEP2: We will find the difference between the quantities of a product with its previous year’s quantity. If this difference is greater than or equal to zero for all the rows, then the product is a constantly increasing in sales. The final query to get the required result is


SELECT PRODUCT_NAME
FROM
(
SELECT P.PRODUCT_NAME,
       S.QUANTITY -
       LEAD(S.QUANTITY,1,0) OVER (
                            PARTITION BY P.PRODUCT_ID
                            ORDER BY S.YEAR DESC
                            ) QUAN_DIFF
FROM   PRODUCTS P,
       SALES S
WHERE  P.PRODUCT_ID = S.PRODUCT_ID
)A
GROUP BY PRODUCT_NAME
HAVING MIN(QUAN_DIFF) >= 0;

PRODUCT_NAME
------------
IPhone


2. Write a SQL query to find the products which does not have sales at all?

Solution:

“LG” is the only product which does not have sales at all. This can be achieved in three ways.

Method1: Using left outer join.


SELECT P.PRODUCT_NAME
FROM   PRODUCTS P
       LEFT OUTER JOIN
       SALES S
ON     (P.PRODUCT_ID = S.PRODUCT_ID);
WHERE  S.QUANTITY IS NULL

PRODUCT_NAME
------------
LG

Method2: Using the NOT IN operator.


SELECT P.PRODUCT_NAME
FROM   PRODUCTS P
WHERE  P.PRODUCT_ID NOT IN
       (SELECT DISTINCT PRODUCT_ID FROM SALES);

PRODUCT_NAME
------------
LG

Method3: Using the NOT EXISTS operator.


SELECT P.PRODUCT_NAME
FROM   PRODUCTS P
WHERE  NOT EXISTS
       (SELECT 1 FROM SALES S WHERE S.PRODUCT_ID = P.PRODUCT_ID);

PRODUCT_NAME
------------
LG


3. Write a SQL query to find the products whose sales decreased in 2012 compared to 2011?

Solution:

Here Nokia is the only product whose sales decreased in year 2012 when compared with the sales in the year 2011. The SQL query to get the required output is


SELECT P.PRODUCT_NAME
FROM   PRODUCTS P,
       SALES S_2012,
       SALES S_2011
WHERE  P.PRODUCT_ID = S_2012.PRODUCT_ID
AND    S_2012.YEAR = 2012
AND    S_2011.YEAR = 2011
AND    S_2012.PRODUCT_ID = S_2011.PRODUCT_ID
AND    S_2012.QUANTITY < S_2011.QUANTITY;
PRODUCT_NAME
------------
Nokia

4. Write a query to select the top product sold in each year?

Solution:

Nokia is the top product sold in the year 2010. Similarly, Samsung in 2011 and IPhone, Samsung in 2012. The query for this is


SELECT PRODUCT_NAME,
       YEAR
FROM
(
SELECT P.PRODUCT_NAME,
       S.YEAR,
       RANK() OVER (
              PARTITION BY S.YEAR
              ORDER BY S.QUANTITY DESC
              ) RNK
FROM   PRODUCTS P,
       SALES S
WHERE  P.PRODUCT_ID = S.PRODUCT_ID
) A
WHERE RNK = 1;

PRODUCT_NAME YEAR
--------------------
Nokia        2010
Samsung      2011
IPhone       2012
Samsung      2012

5. Write a query to find the total sales of each product.?

Solution:

This is a simple query. You just need to group by the data on PRODUCT_NAME and then find the sum of sales.

SELECT P.PRODUCT_NAME,
       NVL( SUM( S.QUANTITY*S.PRICE ), 0) TOTAL_SALES
FROM   PRODUCTS P
       LEFT OUTER JOIN
       SALES S
ON     (P.PRODUCT_ID = S.PRODUCT_ID)
GROUP BY P.PRODUCT_NAME;

PRODUCT_NAME TOTAL_SALES
---------------------------
LG            0
IPhone        405000
Samsung       406000
Nokia         245000
1. Write a query to find the products whose quantity sold in a year should be greater than the average quantity of the product sold across all the years?

Solution:

This can be solved with the help of correlated query. The SQL query for this is


SELECT P.PRODUCT_NAME,
       S.YEAR,
       S.QUANTITY
FROM   PRODUCTS P,
       SALES S
WHERE  P.PRODUCT_ID = S.PRODUCT_ID
AND    S.QUANTITY >
       (SELECT AVG(QUANTITY)
       FROM SALES S1
       WHERE S1.PRODUCT_ID = S.PRODUCT_ID
       );

PRODUCT_NAME YEAR QUANTITY
--------------------------
Nokia        2010    25
IPhone       2012    20
Samsung      2012    20
Samsung      2010    20

2. Write a query to compare the products sales of "IPhone" and "Samsung" in each year? The output should look like as


YEAR IPHONE_QUANT SAM_QUANT IPHONE_PRICE SAM_PRICE
---------------------------------------------------
2010   10           20       9000         7000
2011   15           18       9000         7000
2012   20           20       9000         7000

Solution:

By using self-join SQL query we can get the required result. The required SQL query is


SELECT S_I.YEAR,
       S_I.QUANTITY IPHONE_QUANT,
       S_S.QUANTITY SAM_QUANT,
       S_I.PRICE    IPHONE_PRICE,
       S_S.PRICE    SAM_PRICE
FROM   PRODUCTS P_I,
       SALES S_I,
       PRODUCTS P_S,
       SALES S_S
WHERE  P_I.PRODUCT_ID = S_I.PRODUCT_ID
AND    P_S.PRODUCT_ID = S_S.PRODUCT_ID
AND    P_I.PRODUCT_NAME = 'IPhone'
AND    P_S.PRODUCT_NAME = 'Samsung'
AND    S_I.YEAR = S_S.YEAR

3. Write a query to find the ratios of the sales of a product? 

Solution:

The ratio of a product is calculated as the total sales price in a particular year divide by the total sales price across all years. Oracle provides RATIO_TO_REPORT analytical function for finding the ratios. The SQL query is 


SELECT P.PRODUCT_NAME,
       S.YEAR,
       RATIO_TO_REPORT(S.QUANTITY*S.PRICE)
         OVER(PARTITION BY P.PRODUCT_NAME ) SALES_RATIO
FROM   PRODUCTS P,
       SALES S
WHERE (P.PRODUCT_ID = S.PRODUCT_ID);

PRODUCT_NAME YEAR      RATIO
-----------------------------
IPhone       2011   0.333333333
IPhone       2012   0.444444444
IPhone       2010   0.222222222
Nokia        2012   0.163265306
Nokia        2011   0.326530612
Nokia        2010   0.510204082
Samsung      2010   0.344827586
Samsung      2012   0.344827586
Samsung      2011   0.310344828

4. In the SALES table quantity of each product is stored in rows for every year. Now write a query to transpose the quantity for each product and display it in columns? The output should look like as 


PRODUCT_NAME QUAN_2010 QUAN_2011 QUAN_2012
------------------------------------------
IPhone       10        15        20
Samsung      20        18        20
Nokia        25        16        8

Solution:

Oracle 11g provides a pivot function to transpose the row data into column data. The SQL query for this is 


SELECT * FROM
(
SELECT P.PRODUCT_NAME,
       S.QUANTITY,
       S.YEAR
FROM   PRODUCTS P,
       SALES S
WHERE (P.PRODUCT_ID = S.PRODUCT_ID)
)A
PIVOT ( MAX(QUANTITY) AS QUAN FOR (YEAR) IN (2010,2011,2012));

If you are not running oracle 11g database, then use the below query for transposing the row data into column data. 


SELECT P.PRODUCT_NAME,
       MAX(DECODE(S.YEAR,2010, S.QUANTITY)) QUAN_2010,
       MAX(DECODE(S.YEAR,2011, S.QUANTITY)) QUAN_2011,
       MAX(DECODE(S.YEAR,2012, S.QUANTITY)) QUAN_2012
FROM   PRODUCTS P,
       SALES S
WHERE (P.PRODUCT_ID = S.PRODUCT_ID)
GROUP BY P.PRODUCT_NAME;

5. Write a query to find the number of products sold in each year?

Solution:

To get this result we have to group by on year and the find the count. The SQL query for this question is


SELECT YEAR,
       COUNT(1) NUM_PRODUCTS
FROM   SALES
GROUP BY YEAR;

YEAR  NUM_PRODUCTS
------------------
2010      3
2011      3
2012      3
1. Write a query to generate sequence numbers from 1 to the specified number N?

Solution:


SELECT LEVEL FROM DUAL CONNECT BY LEVEL<=&N;

2. Write a query to display only friday dates from Jan, 2000 to till now?

Solution:


SELECT  C_DATE,
        TO_CHAR(C_DATE,'DY')
FROM
(
  SELECT TO_DATE('01-JAN-2000','DD-MON-YYYY')+LEVEL-1 C_DATE
  FROM   DUAL
  CONNECT BY LEVEL <=
       (SYSDATE - TO_DATE('01-JAN-2000','DD-MON-YYYY')+1)
)
WHERE TO_CHAR(C_DATE,'DY') = 'FRI';

3. Write a query to duplicate each row based on the value in the repeat column? The input table data looks like as below


Products, Repeat
----------------
A,         3
B,         5
C,         2

Now in the output data, the product A should be repeated 3 times, B should be repeated 5 times and C should be repeated 2 times. The output will look like as below


Products, Repeat
----------------
A,        3
A,        3
A,        3
B,        5
B,        5
B,        5
B,        5
B,        5
C,        2
C,        2

Solution:


SELECT PRODUCTS,
       REPEAT
FROM   T,
      ( SELECT LEVEL L FROM DUAL
        CONNECT BY LEVEL <= (SELECT MAX(REPEAT) FROM T)
      ) A
WHERE T.REPEAT >= A.L
ORDER BY T.PRODUCTS;

4. Write a query to display each letter of the word "SMILE" in a separate row?


S
M
I
L
E

Solution:


SELECT SUBSTR('SMILE',LEVEL,1) A
FROM   DUAL
CONNECT BY LEVEL <=LENGTH('SMILE');

5. Convert the string "SMILE" to Ascii values?  The output should look like as 83,77,73,76,69. Where 83 is the ascii value of S and so on.
The ASCII function will give ascii value for only one character. If you pass a string to the ascii function, it will give the ascii value of first letter in the string. Here i am providing two solutions to get the ascii values of string.

Solution1:


SELECT SUBSTR(DUMP('SMILE'),15)
FROM DUAL;

Solution2:


SELECT WM_CONCAT(A)
FROM
(
SELECT ASCII(SUBSTR('SMILE',LEVEL,1)) A
FROM   DUAL
CONNECT BY LEVEL <=LENGTH('SMILE')
);
1. Consider the following friends table as the source


Name, Friend_Name
-----------------
sam,   ram
sam,   vamsi
vamsi, ram
vamsi, jhon
ram,   vijay
ram,   anand

Here ram and vamsi are friends of sam; ram and jhon are friends of vamsi and so on. Now write a query to find friends of friends of sam. For sam; ram,jhon,vijay and anand are friends of friends. The output should look as


Name, Friend_of_Firend
----------------------
sam,    ram
sam,    jhon
sam,    vijay
sam,    anand

Solution:


SELECT  f1.name,
        f2.friend_name as friend_of_friend
FROM    friends f1,
        friends f2
WHERE   f1.name = 'sam'
AND     f1.friend_name = f2.name;

2. This is an extension to the problem 1. In the output, you can see ram is displayed as friends of friends. This is because, ram is mutual friend of sam and vamsi. Now extend the above query to exclude mutual friends. The outuput should look as


Name, Friend_of_Friend
----------------------
sam,    jhon
sam,    vijay
sam,    anand

Solution:


SELECT  f1.name,
        f2.friend_name as friend_of_friend
FROM    friends f1,
        friends f2
WHERE   f1.name = 'sam'
AND     f1.friend_name = f2.name
AND     NOT EXISTS
        (SELECT 1 FROM friends f3
         WHERE f3.name = f1.name
         AND   f3.friend_name = f2.friend_name);

3. Write a query to get the top 5 products based on the quantity sold without using the row_number analytical function? The source data looks as


Products, quantity_sold, year
-----------------------------
A,         200,          2009
B,         155,          2009
C,         455,          2009
D,         620,          2009
E,         135,          2009
F,         390,          2009
G,         999,          2010
H,         810,          2010
I,         910,          2010
J,         109,          2010
L,         260,          2010
M,         580,          2010

Solution:


SELECT  products,
        quantity_sold,
        year
FROM
(
  SELECT  products,
          quantity_sold,
          year,
          rownum r
  from    t
  ORDER BY quantity_sold DESC
)A
WHERE r <= 5;

4. This is an extension to the problem 3. Write a query to produce the same output using row_number analytical function?

Solution:


SELECT  products,
        quantity_sold,
        year
FROM
(
  SELECT products,
         quantity_sold,
         year,
         row_number() OVER(
            ORDER BY quantity_sold DESC) r
  from   t
)A
WHERE r <= 5;

5. This is an extension to the problem 3. write a query to get the top 5 products in each year based on the quantity sold?

Solution:


SELECT  products,
        quantity_sold,
        year
FROM
(
   SELECT products,
          quantity_sold,
          year,
          row_number() OVER(
               PARTITION BY year
               ORDER BY quantity_sold DESC) r
   from   t
)A
WHERE r <= 5;
1. Load the below products table into the target table.

CREATE TABLE PRODUCTS
(
       PRODUCT_ID     INTEGER,
       PRODUCT_NAME   VARCHAR2(30)
);

INSERT INTO PRODUCTS VALUES ( 100, 'Nokia');
INSERT INTO PRODUCTS VALUES ( 200, 'IPhone');
INSERT INTO PRODUCTS VALUES ( 300, 'Samsung');
INSERT INTO PRODUCTS VALUES ( 400, 'LG');
INSERT INTO PRODUCTS VALUES ( 500, 'BlackBerry');
INSERT INTO PRODUCTS VALUES ( 600, 'Motorola');
COMMIT;

SELECT * FROM PRODUCTS;

PRODUCT_ID PRODUCT_NAME
-----------------------
100        Nokia
200        IPhone
300        Samsung
400        LG
500        BlackBerry
600        Motorola

The requirements for loading the target table are:

·                     Select only 2 products randomly.
·                     Do not select the products which are already loaded in the target table with in the last 30 days.
·                     Target table should always contain the products loaded in 30 days. It should not contain the products which are loaded prior to 30 days.
Solution:

First we will create a target table. The target table will have an additional column INSERT_DATE to know when a product is loaded into the target table. The target 
table structure is

CREATE TABLE TGT_PRODUCTS
(
       PRODUCT_ID     INTEGER,
       PRODUCT_NAME   VARCHAR2(30),
       INSERT_DATE    DATE
);

The next step is to pick 5 products randomly and then load into target table. While selecting check whether the products are there in the 

INSERT INTO TGT_PRODUCTS
SELECT  PRODUCT_ID,
        PRODUCT_NAME,
        SYSDATE INSERT_DATE
FROM
(
SELECT  PRODUCT_ID,
 PRODUCT_NAME
FROM PRODUCTS S
WHERE   NOT EXISTS (
           SELECT 1
           FROM   TGT_PRODUCTS T
           WHERE  T.PRODUCT_ID = S.PRODUCT_ID
        )
ORDER BY DBMS_RANDOM.VALUE --Random number generator in oracle.
)A
WHERE ROWNUM <= 2;

The last step is to delete the products from the table which are loaded 30 days back. 

DELETE FROM TGT_PRODUCTS
WHERE  INSERT_DATE < SYSDATE - 30;

2. Load the below CONTENTS table into the target table. 

CREATE TABLE CONTENTS
(
  CONTENT_ID  INTEGER,
  CONTENT_TYPE VARCHAR2(30)
);

INSERT INTO CONTENTS VALUES (1,'MOVIE');
INSERT INTO CONTENTS VALUES (2,'MOVIE');
INSERT INTO CONTENTS VALUES (3,'AUDIO');
INSERT INTO CONTENTS VALUES (4,'AUDIO');
INSERT INTO CONTENTS VALUES (5,'MAGAZINE');
INSERT INTO CONTENTS VALUES (6,'MAGAZINE');
COMMIT;

SELECT * FROM CONTENTS;

CONTENT_ID CONTENT_TYPE
-----------------------
1          MOVIE
2          MOVIE
3          AUDIO
4          AUDIO
5          MAGAZINE
6          MAGAZINE

The requirements to load the target table are: 

·                     Load only one content type at a time into the target table.
·                     The target table should always contain only one contain type.
·                     The loading of content types should follow round-robin style. First MOVIE, second AUDIO, Third MAGAZINE and again fourth Movie.

Solution: 

First we will create a lookup table where we mention the priorities for the content types. The lookup table “Create Statement” and data is shown below. 

CREATE TABLE CONTENTS_LKP
(
  CONTENT_TYPE VARCHAR2(30),
  PRIORITY     INTEGER,
  LOAD_FLAG  INTEGER
);

INSERT INTO CONTENTS_LKP VALUES('MOVIE',1,1);
INSERT INTO CONTENTS_LKP VALUES('AUDIO',2,0);
INSERT INTO CONTENTS_LKP VALUES('MAGAZINE',3,0);
COMMIT;

SELECT * FROM CONTENTS_LKP;

CONTENT_TYPE PRIORITY LOAD_FLAG
---------------------------------
MOVIE         1          1
AUDIO         2          0
MAGAZINE      3          0

Here if LOAD_FLAG is 1, then it indicates which content type needs to be loaded into the target table. Only one content type will have LOAD_FLAG as 1. The other content types will have LOAD_FLAG as 0. The target table structure is same as the source table structure. 

The second step is to truncate the target table before loading the data 

TRUNCATE TABLE TGT_CONTENTS;

The third step is to choose the appropriate content type from the lookup table to load the source data into the target table. 

INSERT INTO TGT_CONTENTS
SELECT  CONTENT_ID,
 CONTENT_TYPE
FROM CONTENTS
WHERE CONTENT_TYPE = (SELECT CONTENT_TYPE FROM CONTENTS_LKP WHERE LOAD_FLAG=1);

The last step is to update the LOAD_FLAG of the Lookup table. 

UPDATE CONTENTS_LKP
SET LOAD_FLAG = 0
WHERE LOAD_FLAG = 1;

UPDATE CONTENTS_LKP
SET LOAD_FLAG = 1
WHERE PRIORITY = (
SELECT DECODE( PRIORITY,(SELECT MAX(PRIORITY) FROM CONTENTS_LKP) ,1 , PRIORITY+1)
FROM   CONTENTS_LKP
WHERE  CONTENT_TYPE = (SELECT DISTINCT CONTENT_TYPE FROM TGT_CONTENTS)
);

SQL Queries Interview Questions - Oracle Analytical Functions Part 1

Analytic functions compute aggregate values based on a group of rows. They differ from aggregate functions in that they return multiple rows for each group. Most of the SQL developers won't use analytical functions because of its cryptic syntax or uncertainty about its logic of operation. Analytical functions saves lot of time in writing queries and gives better performance when compared to native SQL. 

Before starting with the interview questions, we will see the difference between the aggregate functions and analytic functions with an example. I have used SALES TABLE as an example to solve the interview questions. Please create the below sales table in your oracle database.



CREATE TABLE SALES
(
       SALE_ID        INTEGER,
       PRODUCT_ID     INTEGER,
       YEAR           INTEGER,
       Quantity       INTEGER,
       PRICE          INTEGER
);
 
INSERT INTO SALES VALUES ( 1, 100, 2008, 10, 5000);
INSERT INTO SALES VALUES ( 2, 100, 2009, 12, 5000);
INSERT INTO SALES VALUES ( 3, 100, 2010, 25, 5000);
INSERT INTO SALES VALUES ( 4, 100, 2011, 16, 5000);
INSERT INTO SALES VALUES ( 5, 100, 2012, 8,  5000);
 
INSERT INTO SALES VALUES ( 6, 200, 2010, 10, 9000);
INSERT INTO SALES VALUES ( 7, 200, 2011, 15, 9000);
INSERT INTO SALES VALUES ( 8, 200, 2012, 20, 9000);
INSERT INTO SALES VALUES ( 9, 200, 2008, 13, 9000);
INSERT INTO SALES VALUES ( 10,200, 2009, 14, 9000);
 
INSERT INTO SALES VALUES ( 11, 300, 2010, 20, 7000);
INSERT INTO SALES VALUES ( 12, 300, 2011, 18, 7000);
INSERT INTO SALES VALUES ( 13, 300, 2012, 20, 7000);
INSERT INTO SALES VALUES ( 14, 300, 2008, 17, 7000);
INSERT INTO SALES VALUES ( 15, 300, 2009, 19, 7000);
COMMIT;
 
SELECT * FROM SALES;
 
SALE_ID PRODUCT_ID YEAR QUANTITY PRICE
--------------------------------------
1       100        2008   10     5000
2       100        2009   12     5000
3       100        2010   25     5000
4       100        2011   16     5000
5       100        2012   8      5000
6       200        2010   10     9000
7       200        2011   15     9000
8       200        2012   20     9000
9       200        2008   13     9000
10      200        2009   14     9000
11      300        2010   20     7000
12      300        2011   18     7000
13      300        2012   20     7000
14      300        2008   17     7000
15      300        2009   19     7000
 


Difference Between Aggregate and Analytic Functions:

Q. Write a query to find the number of products sold in each year?

The SQL query Using Aggregate functions is


SELECT  Year,
 COUNT(1) CNT
FROM SALES
GROUP BY YEAR;
 
YEAR  CNT
---------
2009  3
2010  3
2011  3
2008  3
2012  3


The SQL query Using Aanalytic functions is


SELECT  SALE_ID,
 PRODUCT_ID,
 Year,
 QUANTITY,
 PRICE,
 COUNT(1) OVER (PARTITION BY YEAR) CNT
FROM SALES;
 
SALE_ID PRODUCT_ID YEAR QUANTITY PRICE CNT
------------------------------------------
9       200        2008   13     9000 3
1       100        2008   10     5000 3
14      300        2008   17     7000 3
15      300        2009   19     7000 3
2       100        2009   12     5000 3
10      200        2009   14     9000 3
11      300        2010   20     7000 3
6       200        2010   10     9000 3
3       100        2010   25     5000 3
12      300        2011   18     7000 3
4       100        2011   16     5000 3
7       200        2011   15     9000 3
13      300        2012   20     7000 3
5       100        2012   8      5000 3
8       200        2012   20     9000 3


From the ouputs, you can observe that the aggregate functions return only one row per group whereas analytic functions keeps all the rows in the gorup. Using the aggregate functions, the select clause contains only the columns specified in group by clause and aggregate functions whereas in analytic functions you can specify all the columns in the table.

The PARTITION BY clause is similar to GROUP By clause, it specifies the window of rows that the analytic funciton should operate on.

I hope you got some basic idea about aggregate and analytic functions. Now lets start with solving the Interview Questions on Oracle Analytic Functions.

1. Write a SQL query using the analytic function to find the total sales(QUANTITY) of each product?

Solution:

SUM analytic function can be used to find the total sales. The SQL query is


SELECT  PRODUCT_ID,
 QUANTITY,
 SUM(QUANTITY) OVER( PARTITION BY PRODUCT_ID ) TOT_SALES
FROM SALES;
 
PRODUCT_ID QUANTITY TOT_SALES
-----------------------------
100        12        71
100        10        71
100        25        71
100        16        71
100        8         71
200        15        72
200        10        72
200        20        72
200        14        72
200        13        72
300        20        94
300        18        94
300        17        94
300        20        94
300        19        94

2. Write a SQL query to find the cumulative sum of sales(QUANTITY) of each product? Here first sort the QUANTITY in ascendaing order for each product and then accumulate the QUANTITY.
Cumulative sum of QUANTITY for a product = QUANTITY of current row + sum of QUANTITIES all previous rows in that product.

Solution:

We have to use the option "ROWS UNBOUNDED PRECEDING" in the SUM analytic function to get the cumulative sum. The SQL query to get the ouput is


SELECT PRODUCT_ID,
 QUANTITY,
 SUM(QUANTITY) OVER( PARTITION BY PRODUCT_ID 
  ORDER BY QUANTITY ASC 
  ROWS UNBOUNDED PRECEDING) CUM_SALES
FROM SALES;
 
PRODUCT_ID QUANTITY CUM_SALES
-----------------------------
100        8         8
100        10        18
100        12        30
100        16        46
100        25        71
200        10        10
200        13        23
200        14        37
200        15        52
200        20        72
300        17        17
300        18        35
300        19        54
300        20        74
300        20        94


The ORDER BY clause is used to sort the data. Here the ROWS UNBOUNDED PRECEDING option specifies that the SUM analytic function should operate on the current row and the pervious rows processed.


3. Write a SQL query to find the sum of sales of current row and previous 2 rows in a product group? Sort the data on sales and then find the sum.

Solution:

The sql query for the required ouput is


SELECT PRODUCT_ID,
 QUANTITY,
 SUM(QUANTITY) OVER(
  PARTITION BY PRODUCT_ID 
  ORDER BY QUANTITY DESC 
  ROWS BETWEEN  2 PRECEDING AND CURRENT ROW) CALC_SALES
FROM SALES;
 
 
PRODUCT_ID QUANTITY CALC_SALES
------------------------------
100        25        25
100        16        41
100        12        53
100        10        38
100        8         30
200        20        20
200        15        35
200        14        49
200        13        42
200        10        37
300        20        20
300        20        40
300        19        59
300        18        57
300        17        54

The ROWS BETWEEN clause specifies the range of rows to consider for calculating the SUM.

4. Write a SQL query to find the Median of sales of a product?

Solution:

The SQL query for calculating the median is


SELECT PRODUCT_ID,
        QUANTITY,
       PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY QUANTITY ASC) 
                             OVER (PARTITION BY PRODUCT_ID) MEDIAN
FROM   SALES;
 
PRODUCT_ID QUANTITY MEDIAN
--------------------------
100         8         12
100         10        12
100         12        12
100         16        12
100         25        12
200         10        14
200         13        14
200         14        14
200         15        14
200         20        14
300         17        19
300         18        19
300         19        19
300         20        19
300         20        19

5. Write a SQL query to find the minimum sales of a product without using the group by clause.

Solution:

The SQL query is


SELECT  PRODUCT_ID,
        YEAR,
        QUANTITY
FROM        
(
SELECT PRODUCT_ID,
        YEAR,
        QUANTITY,
        ROW_NUMBER() OVER(PARTITION BY PRODUCT_ID 
  ORDER BY QUANTITY ASC) MIN_SALE_RANK
FROM   SALES
) WHERE MIN_SALE_RANK = 1;
 
 
PRODUCT_ID YEAR QUANTITY
------------------------
100        2012    8
200        2010    10
300        2008    17

Oracle Query to split the delimited data in a column to multiple rows

1. Consider the following table "t" data as the source


id  value
----------
1   A,B,C
2   P,Q,R,S,T
3   M,N

Here the data in value column is a delimited by comma. Now write a query to split the delimited data in the value column into multiple rows. The output should look like as


id value
--------
1   A
1   B
1   C
2   P
2   Q
2   R
2   S
2   T
3   M
3   N

Solution: 


SELECT  t.id,
    CASE  WHEN  a.l = 1
              THEN  substr(value, 1, instr(value,',',1,a.l)-1)
              ELSE  substr(value, instr(value,',',1,a.l-1)+1, 
                              CASE WHEN instr(value,',',1,a.l)-instr(value,',',1,a.l-1)-1 > 0
                                   THEN instr(value,',',1,a.l)-instr(value,',',1,a.l-1)-1
                                   ELSE length(value)
                                   END
     )
        END  final_value
FROM    t, 
        ( SELECT level l 
          FROM DUAL 
          CONNECT BY LEVEL <= 
                (
                SELECT Max(length(value) - length(replace(value,',',''))+1) FROM t
                ) 
        ) a
WHERE   length(value) - length(replace(value,',',''))+1 >= a.l
order by t.id, a.l;

Min and Max values of contiguous rows - Oracle SQL Query

Q) How to find the Minimum and maximum values of continuous sequence numbers in a group of rows.

I know the problem is not clear without giving an example. Let say I have the Employees table with the below data.


Table Name: Employees
Dept_Id Emp_Seq
---------------
10       1
10       2
10       3
10       5
10       6
10       8
10       9
10       11
20       1
20       2

I want to find the minimum and maximum values of continuous Emp_Seq numbers. The output should look as.


Dept_Id Min_Seq Max_Seq
-----------------------
10      1       3
10      5       6
10      8       9
10      11      11
20      1       2

Write an SQL query in oracle to find the minimum and maximum values of continuous Emp_Seq in each department?

STEP1: First we will generate unique sequence numbers in each department using the Row_Number analytic function in the Oracle. The SQL query is.


SELECT  Dept_Id,
        Emp_Seq,
 ROW_NUMBER() OVER (PARTITION BY Dept_Id ORDER BY Emp_Seq) rn
FROM    employees;
 
Dept_Id Emp_Seq  rn
--------------------
10       1       1
10       2       2
10       3       3
10       5       4
10       6       5
10       8       6
10       9       7
10       11      8
20       1       1
20       2       2

STEP2: Subtract the value of rn from emp_seq to identify the continuous sequences as a group. The SQL query is


SELECT  Dept_Id,
        Emp_Seq,
 Emp_Seq-ROW_NUMBER() OVER (PARTITION BY Dept_Id ORDER BY Emp_Seq) Dept_Split
FROM    employees;
 
Dept_Id Emp_Seq  Dept_Split
---------------------------
10       1       0
10       2       0
10       3       0
10       5       1
10       6       1
10       8       2
10       9       2
10       11      3
20       1       0
20       2       0

STEP3: The combination of the Dept_Id and Dept_Split fields will become the group for continuous rows. Now use group by on these fields and find the min and max values. The final SQL query is


SELECT  Dept_Id,
        MIN(Emp_Seq) Min_Seq,
        MAX(Emp_Seq) Max_Seq
FROM
(
SELECT  Dept_Id,
        Emp_Seq,
 Emp_Seq-ROW_NUMBER() OVER (PARTITION BY Dept_Id ORDER BY Emp_Seq) Dept_Split
FROM    employees;
) A
Group BY Dept_Id, Dept_Split

How to find (calculate) median using oracle sql query

A median is a value separating the higher half of sample from the lower half. The median can be found by arranging all the numerical values from lowest to highest value and picking the middle one. If there are even number of numerical values, then there is no single middle value; then the median is defined as the mean of the two middle values.

Now let see how to calculate the median in oracle with the employees table as example.

Table name: employees 

empid, deptid, salary
1,        100,     5000
2,        100,     3000
3,        100,     4000
5,        200,     6000
6,        200,     8000

The below query is used to calculate the median of employee salaries across the entire table. 

select  empid, 
        dept_id, 
        salary, 
        percentile_disc(0.5) within group (order by salary desc) 
        over () median 
from   employees;

The output of the above query is 

empid, deptid, salary, median 
-----------------------------
1,     100,    5000,   5000 
2,     100,    3000,   5000 
3,     100,    4000,   5000 
5,     200,    6000,   5000 
6,     200,    8000,   5000
Now we will write a query to find the median of employee salaries in each department. 

select   empid,  
         dept_id,  
         salary,  
         percentile_disc(0.5) within group (order by salary desc)  
         over (partition by department_id) median  
from   employees;

The output of the above query is

empid, deptid, salary, median
------------------------------
1,     100,    5000,   4000  
2,     100,    3000,   4000  
3,     100,    4000,   4000  
5,     200,    6000,   7000  
6,     200,    8000,   7000

Oracle Query to Repeat a Number n Times

I need to write one query which will accept input parameter from user and display that number of times the user input. For example if I give input parameter as 5 then it should display 55555, if I give 4 it should give output as 4444. 

This question was asked by one of my blog reader. Here is the oracle sql query. 

Solution 

SELECT sys_connect_by_path('','5')  
FROM dual
WHERE level = 5
CONNECT BY level <= 5

Here the connect by level clause is used to repeat the rows n number of times. In our case it repeats the row 5 times. As dual table contains only one record, the connect by clause repeats that single row 5 times. 

SYS_CONNECT_BY_PATH is valid only in hierarchical queries. It returns the path of a column value from root to node, with column values separated by char (In this example empty string) for each row returned by CONNECT BY condition. In our case, it concatenates all the 5's in each row. 

The condition level=5 is used to get only the last row.

Different Ways (How) to Delete Duplicate Rows in Table

In this article I am going to show different ways of deleting duplicating records from the table. It is a common question in interviews which is asked frequently. 

Consider the following table with rows as an example: 

Table Name: Products
 
ProductId Price
---------------
1         10
1         10
2         20
3         30
3         30


Here assume that productId column should be unique after deleting. Now we see how to delete the duplicate records from the products table in different ways. 

1. Using Rowid 

The following Delete statement deletes the rows using the Rowid. 

Syntax:
 
Delete from <tablename>
where rowid not in (select max(rowid) from <tablename> group by <unique columns or primary key>);
 
Example:
 
Delete from products
where rowid not in (select max(rowid) from products group by productid);

2. Using temp table and Distinct 

Here, first create a temp table and insert distinct rows in the temp table. Then truncate the main table and insert records from the temp table. 

Create temporary table products_temp As
Select Distinct ProductID, Price
From   Products;
 
Truncate table Products;
 
Insert into products
Select * 
From   products_temp;

3. Using temp table and Row Number analytic function. 

The row_number analytic function is used to rank the rows. Here we use the row_number function to rank the rows for each group of productId and then select only record from the group. 

Create temporary table products_temp As
Select productid, price
From
(
   Select productid, price,
          row_number() over (partition by productId order by price) group_rank
   From   products
)
Where  group_rank = 1;

Please comment here if you know any other methods of deleting the duplicate records from the table.

SQL Query to Select Numeric Values from Varchar Column in Oracle

Problem Description: One of the columns in the table is of varchar data type. It contains both strings, numbers and mix of char and numeric values. My problem is to print only the values that contains only numeric digits. 

Sample data in the columne is shown below: 

varchar_column
--------------
Ora123
786
database
92db

The output should contain the following values: 

786


Solution1: Using regexp_like function 

The following oracle sql query uses regexp_like function to get only the values that are purely numeric: 

select  varchar_column 
from  table_name
where  regexp_like(varchar_column,'^[0-9]$');

Solution2: Using translate function 

Another way is using both the translate and length function. The sql query is shown below: 

SELECT  varchar_column
FROM    table_name
WHERE   length(translate(varchar_column,'0123456789','1')) is null
AND     varchar_column IS NOT NULL;

String aggregating Analytic Functions in Oracle Database

The string aggregate functions concatenate multiple rows into a single row. Consider the products table as an example. 

Table Name: Products


Year   product
-------------
2010   A
2010   B
2010   C
2010   D
2011   X
2011   Y
2011   Z

Here, in the output we will concatenate the products in each year by a comma separator. The desired output is:


year   product_list
------------------
2010   A,B,C,D
2011   X,Y,Z

LISTAGG analytic function in 11gR2:

The LISTAGG function can be used to aggregate the strings. You can pass the explicit delimiter to the LISTAGG function.


SELECT  year, 
 LISTAGG(product, ',') WITHIN GROUP (ORDER BY product) AS product_list
FROM    products
GROUP BY year;

WM_CONCAT function:

You cannot pass an explicit delimiter to the WM_CONCAT function. It uses comma as the string separator.


SELECT  year,
 wm_concat(product) AS product_list
FROM    products
GROUP BY year;
Pivot and Unpivot Operators in Oracle Database 11g
Pivot: 

The pviot operator converts row data to column data and also can do aggregates while converting. To see how pivot operator works, consider the following "sales" table as any example
Table Name: Sales
customer_id        product        price
--------------------------------------
1                    A             10
1                    B             20
2                    A             30
2                    B             40
2                    C             50
3                    A             60
3                    B             70
3                    C             80
The rows of the "sales" table needs to be converted into columns as shown below

Table Name: sales_rev
cutomer_id  a_product b_product c_product
-----------------------------------------
1           10        20       
2           30        40        50
3           60        70        80

The query for converting the rows to columns is
SELECT *
FROM   (SELECT customer_id,product,price from sales)
pivot  ( sum(price) as total_price for (product) IN ( 'A' as a, 'B' as b, 'C' as c) )
Pivot can be used to generate the data in xml format. The query for generating the data into xml fomat is shown below.
SELECT *
FROM   (SELECT customer_id,product,price from sales)
pivot XML ( sum(price) as total_price for (product) IN ( SELECT distinct product from sales) )

If you are not using oracle 11g database, then you can implement the unpivot feature asconverting rows to columns

Unpivot:

Unpivot operator converts the columns into rows.
Table Name: sales_rev
cutomer_id  a_product  b_product  c_product
-----------------------------------------
1           10         20       
2           30         40         50
3           60         70         80

Table Name: sales
customer_id  product  price
---------------------------
1             A       10
1             B       20
2             A       30
2             B       40
2             C       50
3             A       60
3             B       70
3             C       80

The query to convert rows into columns is
SELECT *
FROM   sales_rev
UNPIVOT [EXCLUDE NULLs | INCLUDE NULLs] (price FOR product IN (a_product AS 'A', b_product AS 'B', c_product_c AS 'C'));

Points to note about the query
·                     The columns price and product in the unpivot clause are required and these names need not to be present in the table.
·                     The unpivoted columns must be specified in the IN clause
·                     By default the query excludes null values.
If you like this post, then please share it by clicking on the +1 button.

Oracle Query to split the delimited data in a column to multiple rows

1. Consider the following table "t" data as the source


id  value
----------
1   A,B,C
2   P,Q,R,S,T
3   M,N

Here the data in value column is a delimited by comma. Now write a query to split the delimited data in the value column into multiple rows. The output should look like as


id value
--------
1   A
1   B
1   C
2   P
2   Q
2   R
2   S
2   T
3   M
3   N

Solution: 


SELECT  t.id,
    CASE  WHEN  a.l = 1
              THEN  substr(value, 1, instr(value,',',1,a.l)-1)
              ELSE  substr(value, instr(value,',',1,a.l-1)+1, 
                              CASE WHEN instr(value,',',1,a.l)-instr(value,',',1,a.l-1)-1 > 0
                                   THEN instr(value,',',1,a.l)-instr(value,',',1,a.l-1)-1
                                   ELSE length(value)
                                   END
     )
        END  final_value
FROM    t, 
        ( SELECT level l 
          FROM DUAL 
          CONNECT BY LEVEL <= 
                (
                SELECT Max(length(value) - length(replace(value,',',''))+1) FROM t
                ) 
        ) a
WHERE   length(value) - length(replace(value,',',''))+1 >= a.l
order by t.id, a.l;

Convert String to Ascii Values in Oracle

The below query converts a string to ascii characters.

select replace(substr(dump('oracle'),instr(dump('oracle'),': ')+2),',') from dual;

The output of this query is 1111149799108101.

Converting Rows to Columns

Lets see the conversion of rows to columns with an example. Suppose we have a products table which looks like

Table: products
product_id
product_name
1
AAA
1
BBB
1
CCC
2
PPP
2
QQQ
2
RRR

Now we want to convert the data in the products table as
product_id
prodcut_name_1
prodcut_name_2
prodcut_name_3
1
AAA
BBB
CCC
2
PPP
QQQ
RRR

The following query converts the rows to columns:

SELECT product_id,
               MAX(DECODE(product_id,1,product_name,NULL)) 
                          product_name_1,
               MAX(DECODE(product_id,1,product_name,NULL)) 
                          product_name_2,
               MAX(DECODE(product_id,1,product_name,NULL)) 
                          product_name_3
FROM    products
GROUP BY  product_id;

Query to Generate Sequence numbers 1 to n

In oracle we can generate sequence numbers from 1 to n by using the below query:

SELECT rownum 
FROM dual 
CONNECT BY LEVEL<=n;

Replace n with a number.

Converting Columns to Rows in Oracle

Lets see the conversion of columns to rows with an example. Suppose we have a table which contains the subjects handled by each teacher. The table looks like

Table: teachers

teacher_id

subject1

subject2

subject3

1

maths

physics

english

2

social

science

drawing

Now we want to convert the data in the teachers table as
teacher_id
subject
1
maths
1
physics
1
english
2
social
2
science
2
drawing

To achieve this we need each row in teachers table to be repeated 3 times (number of subject columns). The following query converts the columns into rows:

SELECT teacher_id,
               CASE pivot
                   WHEN 1
                   THEN subject1
                   WHEN 2
                   THEN subject2
                   WHEN 3
                   THEN subject3
                   ELSE NULL
               END subject
FROM     teachers,
               (SELECT rownum pivot from dual
                CONNECT BY LEVEL <=3)

Concatenating multiple rows into a single column dynamically - Oracle

Description: Concatenating multiple rows into a single column dynamically - Oracle
Q) How to concatenate multiple rows of a column in a table into a single column? 

I have to concatenate multiple rows to a single column. For example consider the below teachers table.


Table Name: Teacher
Teacher_id subject_name
-----------------------
1          Biology
1          Maths
1          Physics
2          English
2          Social

The above table is a normalized table containing the subjects and teacher id. We will denormalize the table, by concatenating the subjects of each teacher into a single column and thus preserving the teacher id as unique in the output. The output data should look like as below


teacher_id subjects_list
-------------------------------
1          Biology|Maths|Physics
2          English|Social

How to achieve this?

Solution:

We can concatenate multiple rows in to a single column dynamically by using the Hierarchical query. The SQL query to get the result is


SELECT teacher_id,
       SUBSTR(SYS_CONNECT_BY_PATH(subject_name, '|'),2) 
           subjects_list
FROM 
(
   SELECT  teacher_id,
           subject_name,
           COUNT(*) OVER (PARTITION BY teacher_id) sub_cnt,
           ROW_NUMBER () OVER (PARTITION BY teacher_id
                    ORDER BY subject_name) sub_seq
   FROM    teachers
) A
WHERE             sub_seq=sub_cnt
START WITH        sub_seq=1
CONNECT BY prior  sub_seq+1=sub_seq
AND prior         teacher_id=teacher_id


No comments:

Post a Comment