How to use subqueries to find both the minimum and maximum value in SQLite - sqlite

I am trying to use subqueries in SQLite to find the minimum and maximum values of my data and return the product name of this min and max. The following lines show two separate queries that will give me the answer, but I would like to combine them like I tried in the third query. However, no results come out of my third query.
-- Select minimum and maximum price
SELECT MIN(UnitPrice), MAX(UnitPrice)
FROM Products;
-- Return productnames
SELECT ProductName, UnitPrice
FROM Products
WHERE UnitPrice = 296.5 OR UnitPrice = 2.5;
-- Combine first 2 queries by using subquery gives no results
SELECT ProductName, UnitPrice
FROM Products
WHERE UnitPrice = (
SELECT MAX(UnitPrice) OR MIN(UnitPrice)
FROM Products
);
I tried several ways of combining the queries, but they all give errors or show no results. Does anyone has any tips? (The assignment is to make use of subqueries, so they would need to be in there)

If you wanted to continue with your subquery approach, you could use:
SELECT ProductName, UnitPrice
FROM Products
WHERE UnitPrice = (SELECT MIN(UnitPrice) FROM Products) OR
UnitPrice = (SELECT MAX(UnitPrice) FROM Products);
You could also use the RANK() analytic function here:
WITH cte AS (
SELECT *, RANK() OVER (ORDER BY UnitPrice) rnk1,
RANK() OVER (ORDER BY UnitPrice DESC) rnk2
FROM Products
)
SELECT ProductName, UnitPrice
FROM cte
WHERE rnk1 = 1 OR rnk2 = 1;

Related

Sqlite SELECT with multiple conditions

I have to create a database with a PRODUCTS table and a CATEGORIES table.
Each product has a name, a price, a creation date and may belong to several categories.
Categories have a name and a flag to indicate whether the category is private or public.
Then I have to select all records that belongs to more than 5 public categories.
I've created the tables like this:
CREATE TABLE PRODUCTS (
ID_PROD int NOT NULL PRIMARY KEY,
NAME TEXT(255),
PRICE INTEGER,
CREATION_DATE DATE
);
CREATE TABLE CATEGORIES (
ID_CAT INTEGER NOT NULL PRIMARY KEY,
NAME TEXT(255),
PRIVATE INTEGER
);
CREATE TABLE PROD_CAT (
ID INTEGER NOT NULL PRIMARY KEY,
ID_PROD INTEGER,
ID_CAT INTEGER,
FOREIGN KEY (ID_PROD) REFERENCES PRODUCTS(ID_PROD),
FOREIGN KEY (ID_CAT) REFERENCES CATEGORIES(ID_CAT)
)
I've managed to select all the records that belongs to more than 5 categories but I can't find out how to add the public category condition...
Here's what I've tried:
This works:
SELECT NAME
FROM PRODUCTS
WHERE ID_PROD IN (SELECT ID_PROD FROM PROD_CAT GROUP BY ID_PROD HAVING COUNT(*)>5)
But not this:
SELECT PRODUCTS.NAME
FROM PRODUCTS, CATEGORIES
WHERE ID_PROD IN (SELECT ID_PROD FROM PROD_CAT GROUP BY ID_PROD HAVING COUNT(*)>5)
AND CATEGORIES.PRIVATE = 1
Any help would be appreciated :)
You need a join of PROD_CAT to CATEGORIES:
SELECT NAME
FROM PRODUCTS
WHERE ID_PROD IN (
SELECT pc.ID_PROD
FROM PROD_CAT pc INNER JOIN CATEGORIES c
ON c.ID_CAT = pc.ID_CAT
WHERE c.PRIVATE -- or WHERE NOT c.PRIVATE for public categories
GROUP BY pc.ID_PROD
HAVING COUNT(*) > 5
)
Or, without the operator IN, with joins of all 3 tables:
SELECT p.ID_PROD, p.NAME
FROM PRODUCTS p
INNER JOIN PROD_CAT pc ON pc.ID_PROD = p.ID_PROD
INNER JOIN CATEGORIES c ON c.ID_CAT = pc.ID_CAT
WHERE c.PRIVATE -- or WHERE NOT c.PRIVATE for public categories
GROUP BY p.ID_PROD, p.NAME
HAVING COUNT(*) > 5
Since this looks like homework, I'll give a good hint.
Your first query returns products belonging to more than 5 categories, using a sub-query for the COUNT. The restriction you added in the second query was added to the top-level WHERE-clause, not the sub-query. The sub-query still works on PROD_CAT and still returns the same results, which may include public categories.

SOQL Query with a Subquery that uses GROUP BY and HAVING throws unknown error

I'm trying to query a list of records from a custom object (SB_User__c) where the value in the Email__c field is not unique.
The following query captures the entire table as expected:
SELECT Id, Name, Email__c, External_Id__c
FROM SB_User__c
ORDER BY Email__c, Name
And my subquery returns a list of Email__c values that are not unique:
SELECT Email__c
FROM SB_User__c
GROUP BY Email__c
HAVING COUNT(Id) > 1
But when these queries are combined, I receive an unknown error:
SELECT Id, Name, Email__c, External_Id__c
FROM SB_User__c
WHERE Email__c IN (
SELECT Email__c FROM SB_User__c
GROUP BY Email__c
HAVING COUNT(Id) > 1)
ORDER BY Email__c, Name
Is there a way to accomplish what I'm trying to without involving apex?
i think the problem is probably in the having clause, i already run a similar query with this editor online:
online editor for sql queries
SELECT * FROM Customers
WHERE CustomerID IN (
SELECT CustomerID FROM Customers
GROUP BY CustomerID
HAVING CustomerID > 50)
ORDER BY Country ASC, CustomerName DESC;
and this query runs just ok, you can check the having clause.

Selecting all max values of column for each distinct value of other column

I am trying to get a list of most used tags for posts on a website on a given day. I currently have this query:
SELECT posts.pdate, tags.tag, count(posts.pid) as post_count
FROM posts, tags
WHERE posts.pid = tags.pid
GROUP BY posts.pdate, tags.tag
ORDER BY posts.pdate;
This provides me with each distinct tag, along with the date they are used on as well as how many posts used them, returning me with this:
2020-09-10|CMPUT291|1
2020-09-10|computing|1
2020-09-10|database|2
2020-09-10|frequentTag1|2
2020-09-10|relational|2
2020-09-10|sql|1
2020-09-10|tieTag1|2
2020-09-11|Database|1
2020-09-11|data|1
2020-09-11|relational|1
2020-09-11|sql|1
2020-09-13|Database|1
2020-09-13|Sql language|1
2020-09-13|access|1
2020-09-13|frequentTag3|2
2020-09-13|query|3
2020-09-13|relational|3
2020-09-13|sql|1
2020-09-17|Database|1
2020-09-17|frequentTag3|3
2020-09-17|query|1
2020-09-17|relational|1
2020-09-17|sql|1
2020-09-17|sql language|1
2020-09-20|RELATIONAL|1
2020-09-20|database|1
2020-09-20|query|1
2020-09-20|sql language|1
2020-09-25|database|1
2020-09-25|sql language|1
2020-09-30|boring|2
2020-09-30|extra tag|1
2020-09-30|fun|3
2020-09-30|just here|1
2020-09-30|more tag|1
2020-09-30|sleep|3
2020-09-30|tag tag|1
2020-09-30|tag test|1
2020-09-30|test tag|1
But, I now need to make it only give me the rows that have the max (or all of them with max in case of a tie) for each date.
I WANT to be able to use MAX(count(posts.pid)) but I know that doesn't work so I need to find an alternative.
I should get a final result of this:
2020-09-10|database|2
2020-09-10|frequentTag1|2
2020-09-10|relational|2
2020-09-10|tieTag1|2
2020-09-11|Database|1
2020-09-11|data|1
2020-09-11|relational|1
2020-09-11|sql|1
2020-09-13|query|3
2020-09-13|relational|3
2020-09-17|frequentTag3|3
2020-09-20|RELATIONAL|1
2020-09-20|database|1
2020-09-20|query|1
2020-09-20|sql language|1
2020-09-25|database|1
2020-09-25|sql language|1
2020-09-30|fun|3
2020-09-30|sleep|3
Any help would be greatly appreciated.
APPLICABLE SCHEMA:
create table posts (
pid char(4),
pdate date,
title text,
body text,
poster char(4),
primary key (pid),
foreign key (poster) references users
);
create table tags (
pid char(4),
tag text,
primary key (pid,tag),
foreign key (pid) references posts
);
You can use RANK() window function:
SELECT pdate, tag, post_count
FROM (
SELECT p.pdate,
t.tag,
COUNT(*) post_count,
RANK() OVER (PARTITION BY p.pdate ORDER BY COUNT(*) DESC) rnk
FROM posts p INNER JOIN tags t
ON p.pid = t.pid
GROUP BY p.pdate, t.tag
)
WHERE rnk = 1
ORDER BY pdate, tag;
You should use a proper JOIN with an ON clause instead of that outdated syntax with the WHERE clause.

Subquery returns more than one in row in PL/SQL stored procedure

I am a complete novice to PL/SQL and this is an attempt to start writing stored procedures . The case is i want to simply find out inside my stored procedure what is the name of the employee who is getting the highest salary for a particular department (which is passed as a parameter to the stored procedure).
Below is a screen shot of my tables:
The code of my stored procedure is as follows :
create or replace procedure High_salary(Dept_Name IN varchar2)
/*RETURN varchar2 */
AS
EMP_NAME_var varchar2(100) := '';
Begin
dbms_output.put_line('****'||Dept_Name);
select EMP_NAME INTO EMP_NAME_var
from(
select EMP_NAME,rank() over(order by salary desc) rn from employee
where DEPT_CD=(select DEPT_CD from DEPARTMENT where DEPT_NAME=Dept_Name)) a where rn=1;
/*RETURN EMP_NAME_var;*/
END;
when i run this i get this error:
Connecting to the database LOCAL_DEV_DB.
ORA-01427: single-row subquery returns more than one row
ORA-06512: at "LOCAL_DEV_DB.HIGH_SALARY", line 7
ORA-06512: at line 6
****'Technology'
Process exited.
Disconnecting from the database LOCAL_DEV_DB.
However when i run the subquery separate it gets only one row as expected :
select EMP_NAME
from(
select EMP_NAME,rank() over(order by salary desc) rn from employee
where DEPT_CD=(select DEPT_CD from DEPARTMENT where DEPT_NAME='Technology')) a where rn=1;
Can someone please point out what i am missing here.
The problem most likely comes from the fact that your variable name is the same as the field name. In the procedure, it is comparing Dept_Name with itself, and unsurprisingly, all rows match.
In the procedure, try naming the variable DeptNameVar or similar, update the reference in the query, and see if that helps.
I would express your logic as a join between the two tables. Then, use ROW_NUMBER to identify the record for a given department corresponding to the employee with the highest salary.
create or replace procedure High_salary (Dept_Name IN varchar2)
AS
EMP_NAME_var varchar2(100) := '';
Begin dbms_output.put_line('****'||Dept_Name);
select EMP_NAME INTO EMP_NAME_var
from
(
select e.EMP_NAME, ROW_NUMBER() OVER (ORDER BY e.SALARY DESC) rn
FROM employee e
INNER JOIN department d
ON e.DEPT_CD = d.DEPT_CD
WHERE d.DEPT_NAME = Dept_Name
) t
where rn = 1
END;
The problem with your current approach is not necessarily the WHERE clause, which should be working, but rather than you are using the RANK function. RANK would return 1 should two or more employees be tied for the highest salary.
By using ROW_NUMBER, you ensure that the subquery would only ever return a single row.
You are getting that error because of this part of the statement
select EMP_NAME,rank() over(order by salary desc) rn from employee.
You are pulling two columns one is EMP_NAME and other is rank() over(order by salary desc) rn.That's why you are getting that error.
Please modify the subquery such that you only select emp_name.

pass curosr variable to subquery in cirsor iterating loop

1 what is my purpose:
I try to get two person from each department with highest salary.
2 how I try to achieve it:
DECLARE
TYPE empl_table IS TABLE OF employees.employee_id%type INDEX BY binary_integer;
empl empl_table;
CURSOR departmennts_id IS
SELECT department_id FROM departments;
BEGIN
FOR depart_row IN departmennts_id
loop
SELECT employee_id BULK COLLECT into empl
FROM
(
SELECT employee_id
FROM employees
where DEPARTMENT_ID= depart_row.department_id
ORDER BY salary DESC
)
WHERE ROWNUM<3;
END loop;
END;
3 where is the problem:
where DEPARTMENT_ID= depart_row.department_id
When I change depart_row.department_id for fixed id number(ex. 80)
query works. If I use depart_row.department_id empl.count is 0.
Where I am making mistake?
For each iteration of your outer cursor you're putting rows into EMPL_TABLE. Each time that the code loops back for another department_id and then re-executes the inner SELECT it replaces the contents of the collection. Thus, if the LAST department seen by the outer cursor happens to have no employees associated with it, you end up with an empty collection.
Your best bet is to eliminate the separate cursor on DEPARTMENTS and just use a single cursor that does everything you want, as in:
SELECT *
FROM (SELECT DEPARTMENT_ID,
SALARY,
ROW_NUMBER() OVER
(PARTITION BY DEPARTMENT_ID
ORDER BY SALARY DESC) AS EMP_RANK
FROM EMPLOYEES
ORDER BY DEPARTMENT_ID, EMP_RANK)
WHERE EMP_RANK < 3
SQLFiddle here.
Share and enjoy.

Resources