Redshift join each values in an array - sql

I have a table like below (its actually the pg_group table)
group_id | group_name | userid
_____________________________________
101 | gr1 | {100,101}
102 | gr2 | {100,110,120}
I have another table where I can see the name of the user id.
userid | username
______________________
100 | user1
101 | user2
110 | user3
120 | user4
I want to join these 2 tables and generate the output like this.
group_id | group_name | username
_____________________________________
101 | gr1 | user1,user2
102 | gr2 | user1,user3,user4
I tried listagg and etc, but it didn't work as expected.
Update:
I tried this one, but list agg seems not working.
SELECT I.group_name, listagg(J.username,',')
FROM pg_group I
LEFT JOIN pg_user J
ON J.userid = ANY(I.userid)
GROUP BY I.group_name
ERROR: One or more of the used functions must be applied on at least one user created tables. Examples of user table only functions are LISTAGG, MEDIAN, PERCENTILE_CONT, etc;

Here first I have converted arrays of user_ID INTO ROWS WITH UNNEST THEN COLLECTED username against those user_id and at last with string_agg() function again those usernames have been grouped into a comma separated column.
select group_id,group_name,string_agg(username,',')usrname from
(select group_id,group_name,unnest(userid::text[])user_id from pg_group )pg
inner join pg_user u
on pg.user_id::int = u.userid
group by group_id,group_name

From googling so far I have understood that you cannot use listagg() if there is no user defined table is involved. I have found a way around. But I cannot check it since I don't have Redshift platform. Please check it out:
select group_name,listagg(username, ', ') within group (order by column_name)
from
(
SELECT I.group_name,J.username
FROM pg_group I
LEFT JOIN pg_user J
ON J.userid = ANY(I.userid)
left join (select top 1 1 from my_schema.my_table)
on 1=1
)
Instead of my_schema.my_table Please use any of your user defined table

Related

Problem with query to select distinct login

I need to select for each cod_user login that didn`t match to his login. Example: a -> acc (where in the table actually is abb).
I need it for some tests in data base in SoapUi.
I start with this, but canĀ“t go any further for now:
SELECT U1.COD_USER, U2.LOGIN
FROM USERS U1
INNER JOIN USER U2
ON U1.LOGIN != U2.LOGIN
table name users
+----------+-------+
| cod_user | login |
+----------+-------+
| a | abb |
| b | acc |
| c | add |
| d | ahh |
| e | agg |
| f | ann |
+----------+-------+
But that query gives me all logins for each users that he didnt use and i only need one. Thanks you.
Does this do what you want?
SELECT U1.COD_USER,
MAX(U1.LOGIN) KEEP (DENSE_RANK FIRST ORDER BY DBMS_RANDOM.RANDOM) as UNUSED_LOGIN
FROM USERS U1
WHERE NOT EXISTS (SELECT 1
FROM USER U2
WHERE U1.COD_USER = U2.COD_USER AND
U1.LOGIN = U2.LOGIN
)
GROUP BY U1.COD_USER
In your both table, you must have a column which match. Like Cod_user must be common field and that needs to be used on join logic. So, just modify the your SQL like below
SELECT U1.COD_USER, U2.LOGIN
FROM USERS U1 INNER JOIN
USER U2
ON ( U1.COD_USER=U2.COD_USER and U1.LOGIN!= U2.LOGIN)
I understand that you want to assign to each user some random login belonging to other user from the same table and this assigned values should be distinct. So:
with t as (
select cod_user, login, count(1) over () cnt,
row_number() over (order by dbms_random.value) rn
from users )
select a.cod_user, a.login, b.login as random_login
from t a
left join t b on a.rn = b.rn + 1 or (a.rn = 1 and b.rn = b.cnt)
order by a.cod_user
dbfiddle demo
I assigned random row numbers to rows, then made self join on a.rn = b.rn + 1. First row must be joined exceptionally to the last, this is why count() over () was used. Probably you could also use mod() for this.
Assignment is random (due to dbms_random used for ordering) and unique. If you run this query severeal times you will get different, random, unique values.

Redundant values while fetching distinct values from column after joins

While I was trying to fetch unique email ids from my postgres database, I am still getting redundant values. The query is as follows :
select distinct(t2.email_id), user_id, registration_date,
last_login, status, count_uo
from (
select t1.*
from (
select distinct(u.email_id), u.user_id,
u.registration_date, u.last_login,
u.status, count(distinct(uo.id)) as count_uo
from users u
join user_offers uo on u.user_id = uo.user_id
and u.email_id != ''
and uo.offer_id in ('13', '9', 18, 7, 19, 25)
join user_utils uu on u.user_id = uu.user_id
and uu.carrier ~* 'Airtel'
or uu.carrier ~* 'Jio'
or uu.carrier ~* 'Idea'
or uu.carrier ~* '!dea'
where u.registration_date::date between date'2016-08-04' and date'2017-09-28'
and u.last_login::date between date'2017-06-01' and date'2017-09-29'
and u.gender = 'm'
and u.status = 'sms-verified'
and u.email_verification_status = 'UN-VERIFIED'
and u.email_id != '' group by u.user_id
) as t1
where t1.count_uo >1 and t1.count_uo < 100
) t2;
I get the output as follows, even after applying distinct twice.
email_id | user_id | registration_date | last_login | status | count_uo
---------------+---------+----------------------------+----------------------------+--------------+----------
abc#gmail.com | 509 | 2017-07-26 16:59:50.608219 | 2017-07-26 17:56:54.88664 | sms-verified | 3
def#gmail.com | 518 | 2017-08-18 19:26:45.217283 | 2017-08-22 15:38:01.591841 | sms-verified | 3
abc#gmail.com | 512 | 2017-08-17 12:01:00.003048 | 2017-08-21 17:52:56.303841 | sms-verified | 3
Since I'm weak in SQL, any help will be appreciated very much.
If you are using Postgres, you can use distinct on:
select distinct on (t2.email_id) t2.email_id, user_id,
registration_date, last_login, status, count_uo
from ( . . . ) t2
order by t2.email_id;
You can add a second key to the order by to get a particular row (say the most recent login by using order by t2.email_id, last_login desc).
You have two users (rows) with 'abc#gmail.com' as email_id: Notice that they have distinct value in user_id column (509 and 512).
As #GordonLinoff said, you can hide one of that results by using DISTINCT ON clause. But I figure out that it's not what you want...
I imagine it's more likely you inserted some test data and duplicated 'abc#gmail.com' in it.
This also point out (I think) a mistake in your model definition. (missing UNIQUE constraints over both email_id and user_id columns in your users table to avoid it could happen again I mean).

SQL Query to get mapping of all users to their logins

What's a query that I can use to get a list of all logins associated with each user in SQL Azure?
So far I've found the following two queries to get all users and all logins, but I haven't found any way to see which user goes with which login:
SELECT * from sys.sql_logins -- get all logins
SELECT * from sys.sysusers -- get all users
In case you find it helpful, here's the documentation for the structures of those the tables:
sys.sql_logins:
https://msdn.microsoft.com/en-us/library/ms174355.aspx?f=255&MSPPError=-2147217396
Column names: name, principal_id, sid, type, type_desc, is_disabled, create_date, modify_date, default_database_name, default_language_name, credential_id, is_policy_checked, is_expiration_checked, password_hash
sys.sysusers: https://msdn.microsoft.com/en-us/library/ms179871.aspx
Column names: uid, status, name, sid, roles, createdate, updatedate, altuid, password, gid, environ, hasdbaccess, islogin, isntname, isntgroup, isntuser, issqluser, isaliased, issqlrole, isapprole
It's hard to tell you your correct answer b/c we don't know the structure of your tables. If you share that we can help more. But below should get you to where you need to go.
They way to do it is by a MySQL JOIN. In this case you should use a INNER or OUTER JOIN depending on how your database is structured.
If you have 2 tables that are structured below you can do an FULL OUTER JOIN
[sys.sql_logins]
| sid| userID | name |
| 1 | 1 | ssmith |
| 2 | 2 | bbob |
[sys.sysusers]
| sid| name |
| 1 | Sam Smith |
| 2 | Billy Bob |
You can use the following query to do it
SELECT A.name as userName, B.name as login
FROM sys.sysusers A
FULL OUTER JOIN sys.sql_logins B
ON A.sid = B.sid
This will result in :
| userName | logins |
| Same Smith | ssmith |
| Billy Bob | bbob |
Here is a link to more types of MySQL Joins
https://www.sitepoint.com/understanding-sql-joins-mysql-database/
http://dev.mysql.com/doc/refman/5.7/en/join.html
http://www.w3schools.com/sql/sql_join.asp
I think you can join on the sid, try this (but maybe just select whatever columns you want):
select l.*, u.*
from sys.sql_logins l
join sys.sysusers u on l.sid = u.sid

Is a UNION ALL the correct way to fetch this data?

I have a Postgres 9.3 database with a users and an affiliates table.
users table columns
+----+-------+
| | |
+----+-------+
| id | email |
+----+-------+
affiliates columns
+----+------------------+------------------+--------+
| id | referred_user_id | referrer_user_id | amount |
+----+------------------+------------------+--------+
I tried the following query:
select
users.email as referred_email,
affiliates.amount
from affiliates
JOIN users ON affiliates.referred_user_id = users.id
UNION ALL
select
users.email as referrer_email,
users.id
from affiliates
JOIN users ON affiliates.referrer_user_id = users.id
It produces table with columns:
+----------------+--------+
| referred_email | amount |
+----------------+--------+
But I want a one to one table such as:
+----------------+----------------+--------+
| referrer_email | referred_email | amount |
+----------------+----------------+--------+
where I'm essentially substituting each *_user_id with a referrer_email and a referred_email and then tacking on the corresponding amount. What can I do differently? I thought UNION ALL joined all the columns together.
I thought UNION ALL joined all the columns together.
No, UNION ALL concatenates the rows. If you want to join columns you need to use a JOIN.
That might look like so:
select
rer.email as referrer_email,
red.email as referred_email,
affiliates.amount
from affiliates
JOIN users rer ON affiliates.referrer_user_id = rer.id
JOIN users red ON affiliates.referred_user_id = red.id
I guess you are looking for something like this:
select u1.referred_email as email1, u2.referrer_email as email2, a.amount
from affiliates as a, users as u1, users as u2
where a.referred_user_id = u1.id
and a.referrer_user_id = u2.id

SQL - Group by Elements of Comma Delineation

How can I group by a comma delineated list within a row?
Situation:
I have a view that shows me information on support tickets. Each ticket is assigned to an indefinite number of resources. It might have one name in the resource list, it might have 5.
I would like to aggregate by individual names, so:
| Ticket ID | resource list
+-----------+----------
| 1 | Smith, Fred, Joe
| 2 | Fred
| 3 | Smith, Joe
| 4 | Joe, Fred
Would become:
| Name | # of Tickets
+-----------+----------
| Fred | 3
| Smith | 2
| Joe | 3
I did not design the database, so I am stuck with this awkward resource list column.
I've tried something like this:
SELECT DISTINCT resource_list
, Count(*) AS '# of Tickets'
FROM IEG.vServiceIEG
GROUP BY resource_list
ORDER BY '# of Tickets' DESC
...which gives me ticket counts based on particular combinations, but I'm having trouble getting this one step further to separate that out.
I also have access to a list of these individual names that I could do a join from, but I'm not sure how I would make that work. Previously in reports, I've used WHERE resource_list LIKE '%' + #tech + '%', but I'm not sure how I would iterate through this for all names.
EDIT:
This is my final query that gave me the information I was looking for:
select b.Item, Count(*) AS 'Ticket Count'
from IEG.vServiceIEG a
cross apply (Select * from dbo.Split(REPLACE(a.resource_list, ' ', ''),',')) b
Group by b.Item
order by 2 desc
Check this Post (Function Definition by Romil) for splitting strings into a table:
How to split string and insert values into table in SQL Server
Use it this way :
select b.Item, Count(*) from IEG.vServiceIEG a
cross apply (
Select * from dbo.Split (a.resource_list,',')
) b
Group by b.Item
order by 2 desc