posgres sql : how to select in list of value - postgresql

posgres sql
i have table like this
table: user
user_id
user_name
u1
user1
u2
user2
u3
user3
u4
user4
table : visit
visit_id
user_id
visit_date
v1
u1
2023-01-15
v2
u1
2023-01-16
v3
u2
2023-01-17
v4
u2
2023-01-18
v5
u4
2023-01-19
v6
u4
2023-01-20
v7
u4
2023-01-21
v8
u4
2023-01-22
v9
u4
2023-01-23
v10
u4
2023-01-24
v11
u4
2023-01-25
How can i fillter list of value like this :
after join tale
and i want to :: where user_name in ('user1','user2','user4') and user1 = '2023-01-16' , user2 = '2023-01-17' , user4 = '2023-01-23'
Result
User id
visit_date
user1
2023-01-16
user2
2023-01-17
user4
2023-01-23

Assuming that you want each of the users only on their corresponding, own, specific date, rather than any of these users on any of these dates:
select a.user_name,
b.visit_date
from user a
inner join visit b
using(user_id)
where (a.user_name, b.visit_date)
in (('user1', '2023-01-16'),
('user2', '2023-01-17'),
('user4', '2023-01-23'));
As long as those are not hand-picked user+date pairs, there might be an easier way to specify which pairs to return using Date/Time functions and operators, conditional expressions and window functions - they let you formulate very specific rules like 'for each user, return only their second date and not if it's on a Thursday or the Weekend, and skip leap years'.

Related

Postgres - Find duplicate values after lowering the values

Hello StackOverflow users... I have a tricky situation and I have yet to find an answer. Maybe you can help me.
Database: PostgreSQL 8.4 (can't upgrade)
In this database, there is a users table. Sadly, the usernames that users can provide when they create a user profile is case sensitive, so a username of Alex is not the same as a username of alex.
There is a new system going out and username is no longer case sensitive. I'm trying to find all of the usernames that would be considered duplicates in the old system. This way we can reach out and have them update the usernames manually and then migrate their users to a newer system (without conflict of username).
I have the following query which will show me the counts of each username matching another with the "lower()" function.
select count(*), lower(username)
from users
where deleted = false
group by lower(username) having count(*) > 1
This returns results like the following:
|count|lower |
|-----+--------+
|3 |alex |
|2 |george |
What I need to do is get this data into a temp table and display all of those duplicate users and other details so that we have a list to go through.
I have part of the temp table figured out, but my main issue is: How do I get the distinct values of all of these duplicates? So in the long run, I get results that look like the following (and maybe even without a temp table if possible):
|lower |username|
|-------+--------+
|alex |Alex |
|alex |alex |
|george |georGe |
|george |George |
Restrictions:
I can't change the version of postgres from 8.4
Some duplicates will have more than 2 hits (the most I've seen so far is 3)
Since the users must be informed, there is no way to change the data other then to contact them prior (which is why the list is needed)
I appreciate any suggestions/feedback you may be able to provide.
How about this. Just generate your above list as a CTE, then join with it in the main query:
WITH dups AS (
SELECT lower(username) uname, count(*) ucount
FROM users
WHERE deleted = false
GROUP BY lower(username) HAVING count(*) > 1)
SELECT username, uname, ucount
FROM users INNER JOIN dups ON lower(username) = uname
WHERE deleted = false
ORDER BY ucount DESC, uname ASC;
username | uname | ucount
----------+--------+--------
Alex | alex | 3
alex | alex | 3
ALEX | alex | 3
GeorGe | george | 2
george | george | 2
(5 rows)
Or even simpler if you only want a bare list of the affected users:
SELECT username
FROM users
WHERE deleted = false AND lower(username) IN (
SELECT lower(username)
FROM users
WHERE deleted = false
GROUP BY lower(username) HAVING count(*) > 1)
ORDER BY lower(username) ASC;
username
----------
Alex
alex
ALEX
GeorGe
george
(5 rows)
I would usually use string_agg, but it looks like it's not supported in 8.4. There appears to be a workaround, but note that I haven't tested due to not having a local copy of 8.4 handy. Something like this should work:
select
(max(u1.username)),
array_to_string(array_agg(u2.username), ',') as duplicates
from users u1
inner join users u2 on u1.id < u2.id
and lower(u1.username) = lower(u2.username)
left join users u3 on u1.id > u3.id
and lower(u1.username) = lower(u3.username)
and u3.deleted = false
where u1.deleted = false
and u2.deleted = false
and u3.id is null
group by u1.id;
This will get the "earliest" user by ID (assuming there is a primary key that isn't username. It could be modified to show the actual lowercase username, and then the rest in the duplicates column.
Edit: to show a row for each duplicate:
select
lower(u1.username),
u2.username
from users u1
inner join users u2 on u1.id < u2.id
and lower(u1.username) = lower(u2.username)
left join users u3 on u1.id > u3.id
and lower(u1.username) = lower(u3.username)
and u3.deleted = false
where u1.deleted = false
and u2.deleted = false
and u3.id is null
order by u1.username;

Fetch matching results from the integer array satisfying the condition which is given as text

I've an array of integer data stored in a particular field in the user table. This array represents the groups in which the user belongs. A user can have any number of groups.
ie,
Table: user
user_id | user_name | user_groups
---------+-------------+-------------
1 | harry | {1,2,3}
2 | John | {4,5,6}
Table: Groups
group_id | group_name
------------+--------------
1 | Arts
2 | Science
3 | Security
4 | Sports
(Pardon, It should have been an 1-N relationship). I need to execute a query as follows,
SELECT * from user where user_groups = ANY(x);
where x will be text values Arts,Science,Security,Sports.
So when x= Arts, the result of harry is returned. The database that I'm using is Postgresql8.4
You can use #> contains operator:
SELECT *
FROM Users
WHERE user_groups #> (SELECT ARRAY[group_id]
FROM Groups
WHERE group_name = 'Arts')
SqlFiddleDemo
EDIT:
Is there any way by which I could display user_groups like
{Arts,Science,Security}, instead of {1,2,3}
You could use correlated subquery:
SELECT user_id, user_name, (SELECT array_agg(g.group_name)
FROM Groups g
WHERE ARRAY[g.group_id] <# u.user_groups) AS user_groups
FROM Users u
WHERE user_groups #> (SELECT ARRAY[group_id]
FROM Groups
WHERE group_name = 'Arts')
SqlFiddleDemo2

Get column value from another table in PostgreSQL

I have two tables:
User and Order
A Order belongs to a User, in a way that the Order table has a column called user_id. So, for example, a row in Order table would look like:
id: 4
description: "pair of shoes"
price: 18.40
user_id: 1
And a User row would look like:
id: 1
email: "myemail#gmail.com"
name: "John"
How would I go if I want to get all the Orders, but instead of getting the user id, get the email?
A simple JOIN will do the trick
SELECT o.id,
o.description,
o.price,
u.email
FROM "order" o JOIN "user" u
ON o.user_id = u.id
Sample output:
| ID | DESCRIPTION | PRICE | EMAIL |
--------------------------------------------------
| 4 | pair of shoes | 18.4 | myemail#gmail.com |
Here is SQLFiddle demo
Further reading A Visual Explanation of SQL Joins

How can I feed back a subquery as a boolean column in PostgreSQL?

We store our accounts information in a PostgreSQL database.
Accounts are in the "accounts" table, groups in the "grp" table, and they're tied together by the "account_grp" table, which maps account_id to grp_id.
I'm trying to craft a query which will give me a view which lets me search for whether members of one group are members of another group, i.e. I want an "is_in_foobar_group" column in the view, so I can SELECT * FROM my_view WHERE grp_id = 1234; and get back
username | is_in_foobar_group | grp_id
---------+--------------------+-------
bob | true | 1234
alice | false | 1234
The foobar bit is hardcoded, and will not need to change.
Any suggestions?
Simpler, faster, more convenient:
WITH x AS (SELECT 1234 AS foobar) -- optional, to enter value only once
SELECT a.username
,EXISTS (
SELECT 1 FROM account_grp g
WHERE g.account_id = a.account_id
AND g.grp_id = x.foobar
) AS is_in_foobar_group
,x.foobar AS grp_id
FROM accounts a, x
Maybe using the EXISTS operator would help:
http://www.postgresql.org/docs/9.2/static/functions-subquery.html#FUNCTIONS-SUBQUERY-EXISTS
I'm not sure you can use it in a SELECT statement, and I don't have a PostgreSQL instance to check it.
Worst case you'll have to do 2 queries, something like:
SELECT username, true, grp_id
FROM accounts a INNER JOIN account_grp g1 on a.account_id = g.account_id
WHERE EXIST (SELECT 1 FROM account_grp g2
WHERE g2.account_id = a.account_id and g2.grp_id = [foobar])
UNION
SELECT username, false, grp_id
FROM accounts a INNER JOIN account_grp g1 on a.account_id = g.account_id
WHERE NOT EXIST (SELECT 1 FROM account_grp g2
WHERE g2.account_id = a.account_id and g2.grp_id = [foobar])

T-SQL Compare 2 Usernames

I have 3 tables below. I am trying to compare the roles of each user. That is I want to show that John has 2 roles while Jane has 1. Currently I am only showing the common roles between them.
Sec_role (PK R_KEY, rname)
Role_User(PK R_KEY, PK USER_KEY)
User(PK USER_KEY, Name)
Sample data:
Sec_Role Role_User User
R_Key|rname| R_Key|User_Key User_KEY|NAME
1 |Analyst 1 |1 1 |John
2 |Sysadmin 2 |1 2 |Jane
2 |2
I am trying to get
User | Role | User2 | Role2
John | Analyst | Jane | (Empty because she isn't an analyst)
John | sysadmin| Jane |Sysadmin
SELECT U.Name AS User1,
U2.Name AS User2,
R.rname AS Role1
R2.Rname AS Role2
FROM Sec_Role AS R
LEFT OUTER JOIN Role_User AS RU
ON R.R_Key=RU.R_Key
LEFT OUTER JOIN User AS U
ON U.User_Key=RU.User_Key
LEFT OUTER JOIN Sec_Role AS R2
ON R2.R_Key=R.R_Key <---(I think this is the issue here)
LEFT OUTER JOIN Role_User AS RU2
ON R2.R_Key=RU2.R_Key
LEFT OUTER JOIN User AS U2
ON U2.User_Key=RU2.User_Key
WHERE U.Name ='John'
AND U2.Name='Jane'
Currently I am getting the Intersection of these two Users.
User | Role | User2 | Role2
John | sysadmin| Jane |Sysadmin
WHERE U.Name ='John'
OR U2.Name='Jane'
note an OR not an AND
why the 4 columns
SELECT U.Name AS User,
R.rname AS Role
FROM Sec_Role AS R
LEFT OUTER JOIN Role_User AS RU
ON R.R_Key=RU.R_Key
LEFT OUTER JOIN User AS U
ON U.User_Key=RU.User_Key
where uName in ('','')
order by 1, 2
Try this
SELECT U.Name AS User1,
U2.Name AS User2,
R.rname AS Role1
R2.Rname AS Role2
FROM Sec_Role AS R
LEFT OUTER JOIN Role_User AS RU
ON R.R_Key=RU.R_Key
LEFT OUTER JOIN User AS U
ON U.User_Key=RU.User_Key
and U.Name ='John'
LEFT OUTER JOIN Sec_Role AS R2
ON R2.R_Key=R.R_Key <---(I think this is the issue here)
LEFT OUTER JOIN Role_User AS RU2
ON R2.R_Key=RU2.R_Key
LEFT OUTER JOIN User AS U2
ON U2.User_Key=RU2.User_Key
AND U2.Name='Jane'