Join columns to search string - sql

I have these tables which I would like to query:
create table employees
(
id bigint generated by default as identity (maxvalue 2147483647),
username varchar(100) not null,
password varchar(60) not null,
account_id bigint,
role_id bigint,
first_name varchar(150),
last_name varchar(150),
primary key (id)
);
create table accounts
(
id bigint generated by default as identity,
account_name varchar(150) not null,
account_group_id bigint not null,
primary key (id)
);
Test data:
insert into employees (id, username, password, account_id) values
(1, "test user", "pass", 3),
(2, "test user2 ", "pass", 4);
insert into accounts (id, account_name, account_group_id) values
(1, "main", 3),
(2, "second", 4);
(3, "third", 4);
I need to create a query which searches into table employees by account_name. I tried this:
Example when I send search param second I need to get a row result: test user2
SELECT * FROM common.employees e
WHERE e.??????? iLIKE CONCAT('%', :params, '%')
Do you know how I can join the tables?

You cannot directly parameterize Sql identifier(columns, tables), You can only parameterize values.
Prepared statements can take parameters: values that are substituted into the statement when it is executed.
https://www.postgresql.org/docs/current/sql-prepare.html
In your code. WHERE e.??????? cannot be easily parameterized. You need to use plpgsql functions.
prepare test(text,int) as SELECT e.* FROM employees e
join accounts a on e.account_id = a.id
WHERE a.account_name iLIKE CONCAT('%', $1, '%')
and a.account_group_id = $2;
If your already have test prepare statement in the active session then DEALLOCATE test;
suppose the account_group_id = 1 then:
execute test('third', 1);

Join the 2 tables like that (result here)
SELECT e.* FROM
employees e, accounts a
WHERE
e.account_id = a.id
and a.account_name = 'second'

To include columns account_group_id and account_id into the result you can get as below :
Though e.* will contain all the info that is present in employee table which include account_id as well. So if you want to customized your result set you can do that according to your need:
SELECT e.*,a.account_group_id
FROM employees e
INNER JOIN accounts a ON a.id = e.account_id
WHERE a.account_name = param

If you just use an inner join and join on the account table using the account_id and add a WHERE clause where you only select from employee where the account_name equals your param....which I'm guessing will be a varchar
SELECT e.*, a.account_group_id
FROM employees e
INNER JOIN accounts a ON a.id = e.account_id
WHERE a.account_name = param
or
WHERE a.account_name LIKE '%param%'
but the second may bring back other users as the param could exist in other names.
Also I don't believe the data in your example is correct as surely the account_id would link to the id in the accounts table...so passing second would in fact get you an employee who's account_id is 2.

Related

Fetch Parent Record anyway even if child condition doesn't meet without using ON

I have two tables Employee and Address.
One Employee can have multiple Address.
Here I want to fetch 'active employee details' and 'active address details' of a particular emp_id. I can achieve this by below query :
Table Structure:
Employee(emp_id,name,is_active)
Address(add_id,emp_id,address,is_active)
Query:
SELECT * FROM EMPLOYEE e
LEFT OUTER JOIN ADDRESS a
ON e.emp_id=a.emp_id
WHERE e.is_active='A'
AND a.is_active='A';
Using above query it does not return any employee details if no active address. I want to return active employee details anyways even if it does not have any active address.
Note: as I am using Hibernate looking for a query without using ON . Only Where clause can be used here.
Kindly suggest.
You need to put a.is_active='A' in ON clause not in WHERE clause
SELECT * FROM EMPLOYEE e
LEFT OUTER JOIN ADDRESS a
ON e.emp_id=a.emp_id AND a.is_active='A'
WHERE e.is_active='A';
Since you have restrictions on using condition in on clause you can try below approach. It will return rows where address is active or address is not available (assuming that is_active column is never null in address table).
Schema and insert statements:
create table EMPLOYEES(emp_id int, name varchar(20), is_active varchar(10));
create table Address(add_id int ,emp_id int ,address varchar(50),is_active varchar(10));
insert into EMPLOYEES values (1,'NAME1','A');
insert into Address values(1,1,'addr1','N');
insert into Address values(2,1,'addr1','N');
Query:
SELECT * FROM EMPLOYEES e
LEFT OUTER JOIN (select * from Address where is_active='A') a
ON e.emp_id=a.emp_id
WHERE e.is_active='A'
AND (a.is_active='A' or a.is_active is null);
Output:
EMP_ID
NAME
IS_ACTIVE
ADD_ID
EMP_ID
ADDRESS
IS_ACTIVE
1
NAME1
A
null
null
null
null
db<>fiddle here

Add join to display items based on key

I have these Postgres tables:
create table employees
(
id bigint primary key,
account_id number,
first_name varchar(150),
last_name varchar(150)
);
create table accounts
(
id bigint primary key,
account_name varchar(150) not null
);
I need to search in table employees by account_id and print result the rows which match in table accounts.id. How I can do this using JOIN?
I'm pretty sure this is what you're looking for.
SELECT a.id, a.account_name, e.first_name, e.last_name
FROM employees as e
JOIN accounts as a on a.id = e.account_id
WHERE e.account_id = 3
This will allow you to search for specific account IDs in the employee table and bring back their corresponding account table information.
You can check this with my dbfiddle here - https://www.db-fiddle.com/f/pwzwQTsHuP27UDF17eAQy4/0

Select Statement in SQL inheritance

CREATE TABLE User(
UserID int primary key,
Name varchar,
type int
);
CREATE TABLE Student(
UserID int primary key references User(UserID),
marks int
);
CREATE TABLE Lecture(
UserID int primary key references User(UserID),
salary int
);
Can someone help with with select statement for Student or lecture.
Both Lecture and Student tables are inheriting from User table,So I need to know how insert data and select data from these tables.
To query the tables, this would work:
SELECT u.[Name]
, s.marks
, l.salary
FROM [User] u
INNER JOIN Student s
ON u.UserId = s.UserId
INNER JOIN Lecture l
ON u.UserId = l.UserId;
However, if there are no records in the Student / Lecture tables yet, you should use LEFT Join instead.
As for inserting the data, you would need to use SCOPE_IDENTITY().
Insert into [User] (Name) values ('Melvin')
Get the identity of the UserId
DECLARE #userId INT;
SELECT #userId = SCOPE_IDENTITY ();
INSERT INTO Student
(
UserID
, Marks
)
VALUES
(userId, 5);
Update: Just noticed this was SQL Lite, which I'm not so familiar with, but it looks like it supports last_insert_rowid() instead of SCOPE_IDENTITY (), but you should get the gist of it.
If you want to select Student X:
SELECT *
FROM Student
WHERE UserID = X
If you want to select all Students and their User data, you'll want something like:
SELECT *
FROM User
JOIN Student ON User.UserID = Student.UserID
I don't understand your question but I think you need something like that
select Name, marks
from User as u, Student as s
inner join u.UserID == s.UserID;

Marking users as not active if they don't have required records

I have a simple table model. There are some required "Actions" that every user has to have completed.
User
-Id
-Name
-IsActive
Actions
-Id
-Name
UserActions
-UserID
-ActionID
I want to find all Users who don't have all the Action records in the UserAcitons table. If they don't have all actions records in UserActions, I want to mark IsActive as false.
There could be 20K users so this should be effecient to process.
Looking for the best way to do this without having a cursor.
Assuming rows in UserActions are unique, you can count the actions in the two tables and compare them:
update u
set isactive = (case when num_actions > total_actions then 1 else 0 end)
from users u left join
(select ua.userid, count(*) as num_actions
from useractions ua
group by ua.userid
) ua
on ua.userid = u.id cross join
(select count(*) as total_actions
from actions a
) a;
SQL Server doesn't support booleans, so this uses 0 for false and 1 for true.
You forgot to tell us some details, for example if there are duplicated actions, if ids are UNIQUE, etc.
for very simple scenario I managed to create the below example:
create table [User]
(
Id int not null primary key,
Name varchar(50) not null,
IsActive bit not null
)
create table [Actions]
(
Id int not null primary key,
Name varchar(50) not null
)
GO
create table [UserActions]
(
UserId int not null,
ActionId int not null,
foreign key (UserId) REFERENCES [User](Id),
foreign key (ActionId) REFERENCES [Actions](Id)
)
GO
insert into [User] values
(1, 'Alice', 1),(2, 'Bob', 1),(3, 'Caroline', 1)
insert into [Actions] values
(1, 'eat'),(2,'drink'),(3,'sleep')
insert into [UserActions] values
(1,1),(1,2),(1,3),
(2,1),(2,2),
(3,1),(3,2),(3,1)
GO
update us
set us.IsActive = 0
from [User] us
join
(
select ua.UserId, COUNT(distinct ua.ActionId) as ActionCount
from [UserActions] ua
group by ua.UserId
) as uac on uac.UserId = us.Id
where uac.ActionCount < (select count(*) from [Actions])
select * from [User] us
Provides de results below
Id Name IsActive
----------- ---------- --------
1 Alice 1
2 Bob 0
3 Caroline 0

Reuse result from subquery when creating a view

Is it possible to reuse the result from a subquery in a subsequent subquery when creating a view in postgresql?
For example I have the following two tables:
CREATE TABLE application
(
id INT PRIMARY KEY,
name CHARACTER VARYING(255)
);
CREATE TABLE application_user
(
id INT PRIMARY KEY,
application_id INT REFERENCES application (id) ON DELETE CASCADE,
active BOOLEAN
);
-- some sample data
INSERT INTO application (id, name) VALUES
(10, 'application1'),
(20, 'application2'),
(30, 'application3');
INSERT INTO application_user (id, application_id, active) VALUES
(1, 10, true),
(2, 10, false),
(3, 20, false),
(4, 20, false),
(5, 20, false);
The view that I need looks (right now) as follows:
CREATE VIEW application_stats AS
SELECT a.name,
(SELECT COUNT(1) FROM application_user u
WHERE a.id = u.application_id) AS users,
(SELECT COUNT(1) FROM application_user u
WHERE a.id = u.application_id AND u.active = true) AS active_users
FROM application a;
This does give me the correct result:
name users active_users
application1 2 1
application2 3 0
application3 0 0
However it is also pretty inefficient since I'm using two times almost the same query and ideally I would like to reuse the result from the first query. Is there an efficient way to do this?
This would normally be expressed as a join/group by:
SELECT a.name, COUNT(au.application_id) as users,
SUM( (au.active = true)::int) as active_users
FROM application a LEFT JOIN
application_user au
ON a.name = au.application_id
GROUP BY a.name;
I'm rather surprised that application doesn't have a serial primary key. But because you are using name, perhaps the join is not needed at all:
SELECT au.application_id, COUNT(*) as users,
SUM( (au.active = true)::int) as active_users
FROM application_user au
GROUP BY au.application_id;
This will return applications that have at least one server.
You should join the two tables, group by application_id and use count with a FILTER (WHERE ...) clause to count only the rows you want:
CREATE VIEW application_stats AS
SELECT a.name
count(*) AS users,
count(*) FILTER (WHERE u.active) AS active_users
FROM application a
LEFT JOIN application_user u ON a.id = u.application_id
GROUP BY a.id;