Problem with query to select distinct login - sql

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.

Related

Redshift join each values in an array

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

Select associated records based on comparison with array column

Suppose a hypothetical scenario with following tables
Vehicles
+----+--------+
| id | number |
+----+--------+
| 1 | v1 |
| 2 | v2 |
| 3 | v3 |
+----+--------+
Users
+-----+------+-------------+
| id | name | vehicle_ids |
+-----+------+-------------+
| 100 | u1 | {1} |
| 200 | u2 | {1,2} |
| 300 | u3 | {2,3} |
| 400 | u4 | {} |
+-----+------+-------------+
Given number of a vehicle I am looking for a query that returns all the users associated with that vehicle,
where vehicle_ids contains the id of vehicle
where vehicle_ids only has the id of vehicle (i.e single element)
We can use ANY and ALL constructs (more details here Row and Array Comparisons)
USING "ANY"
http://sqlfiddle.com/#!17/ec886/21
For first case where we need to return users that are associated with vehicle represented by search query (along with others) we can use ANY. So, following query will return user rows with name u2 and u3 when supplied with v2 as vehicle name
SELECT "users".*
FROM "users"
WHERE (
(SELECT "vehicles"."id" FROM "vehicles" WHERE "vehicles"."number" = 'v2') =
ANY("users"."vehicle_ids")
)
You can replace "number" = 'v2' with any other vehicle name to get all the users which are associated with this vehicle number.
USING "ALL"
http://sqlfiddle.com/#!17/ec886/27
For second case where we need exact match i.e. only one element in the array, we will use All. So following query will return only user row with name u1 when v1
SELECT "users".*
FROM "users"
WHERE (
"users"."vehicle_ids" != '{}' and
(SELECT "vehicles"."id" FROM "vehicles" WHERE "vehicles"."number" = 'v1') = ALL("users"."vehicle_ids")
)
If we don't use "users"."vehicle_ids" != '{}' then the result will also contain user u4
Note:
This subquery method will only work for single vehicle i.e. we can't do WHERE "vehicles"."number" in ('v2', v3) as the subquery will return multiple rows and Postgres will throw an error. We might have to explore unnest if it can serve the same purpose as original question and also if it is generic enough to support aforementioned use case.
You can use unnest() as well:
select u.*
from users u cross join lateral
unnest(vehicle_ids) x(vehicle_id) join
vehicles v
on v.id = x.vehicle_id
where v.number = 'v2';
Here is a db<>fiddle.
Question #1: "where vehicle_ids contains the id of vehicle"
You can join the two tables with a condition on the array
select u.*
from users u
join vehicles v on v.id = any(u.vehicle_ids)
where v.number = 'v1';
Question #2: "where vehicle_ids only has the id of vehicle (i.e single element)"
This can be achieved by create a single element array from a SELECT statement
select u.*
from users u
where u.vehicle_ids = array(select v.id from vehicle v where v.number = 'v1');

A query to return a mix of SUM and COUNT in 5 joined tables

I have a table named Ads, containing one row for each ad.
| ID | AdTitle | AdDescription | ... |
I also have 3 tables named Applications, Referrals and Subscribers, with rows for each application, referral and subscriber associated with an Ad.
| ID | AdID | ApplicantName | ApplicantEmail | ... |
| ID | AdID | ReferrerEmail | ReferralEmail | ... |
| ID | AdID | SubscriberEmail | ... |
Finally I have a table named Views with one row for each ad, containing the total number of views for that ad.
| ID | AdID | Views |
I'm trying to write a query with a summary for each ad in 6 columns: Ad ID, Ad title, number of applications/referrals/subscribers and total views.
A simple query of all tables that I have been working with:
SELECT *
FROM Ads
LEFT JOIN Applications ON Ads.ID = Applications.AdID
LEFT JOIN Referrals ON Ads.ID = Referrals.AdID
LEFT JOIN Subscribers ON Ads.ID = Subscribers.AdID
LEFT JOIN Views ON Ads.ID = Views.AdID
I have tried a lot of combinations of LEFT and INNER joins, GROUP BY, COUNT(...), COUNT(DISTINCT ...) and SUM(CASE ...) but nothing so far have worked. I end up counting NULL values from previous columns, counting entries twice or not at all, counting the number of rows in the Views-table instead of adding them together and so on.
Is it better to split this up in multiple querys, or is there a good way to archive what I want with a single one?
Try this
SELECT *,
(select count (*) from Applications as A1 where A.ID = A1.AdID ) as Applicants,
(select count(*) from Referrals as R where A.ID = R.AdID ) as Referrals,
(select count(*) from Subscribers as S where A.ID = S.AdID ) as Subscribers,
(select count(*) from Views as V where A.ID = V.AdID ) as Views
FROM Ads as A

Summing cost by id that appears on multiple rows

SOLUTION
I solved it by simple doing the following.
SELECT table_size, sum(cost) as total_cost, sum(num_players) as num_players
FROM
(
SELECT table_size, cost, sum(tp.uid) as num_players
FROM tournament as t
LEFT JOIN takes_part AS tp ON tp.tid = t.tid
LEFT JOIN users as u on u.uid = tp.tid
JOIN attributes as a on a.aid = t.attrId
GROUP BY t.tid
) as res
GROUP BY table_size
I wasn't sure it would work, what with the other aggregate functions that I had to use in my real sql, but it seems to be working ok. There may be problems in the future if I want to do other kind of calculations, for instance do a COUNT(DISTINCT tp.uid) over all tournaments. Still, in this case that is not all that important so I am satisfied for now. Thank you all for your help.
UPDATE!!!
Here is a Fiddle that explains the problem:
http://www.sqlfiddle.com/#!2/e03ff/7
I want to get:
table_size | cost
-------------------------------
5 | 110
8 | 80
OLD POST
I'm sure that there is an easy solution to this that I'm just not seeing, but I can't seem to find a solution to it anywhere. What I'm trying to do is the following:
I need to sum 'costs' per tournament in a system. For other reasons, I've had to join with lots of other tables, making the same cost appear on multiple rows, like so:
id | name | cost | (hidden_id)
-----------------------------
0 | Abc | 100 | 1
1 | ASD | 100 | 1
2 | Das | 100 | 1
3 | Ads | 50 | 2
4 | Ads | 50 | 2
5 | Fsd | 0 | 3
6 | Ads | 0 | 3
7 | Dsa | 0 | 3
The costs in the table above are linked to an id value that is not necessary selected in by the SQL (this depends on what the user decides at runtime). What I want to get, is the sum 100+50+0 = 150. Of course, if I just use SUM(cost) I will get a different answer. I tried using SUM(cost)/COUNT(*)*COUNT(tourney_ids) but this only gives correct result under certain circumstances. A (very) simple form of query looks like this:
SELECT SUM(cost) as tot_cost -- This will not work as it sums all rows where the sum appears.
FROM t
JOIN ta ON t.attr_id = ta.toaid
JOIN tr ON tr.toid = t.toid -- This row will cause multiple rows with same cost
GROUP BY *selected by user* -- This row enables the user to group by several attributes, such as weekday, hour or ids of different kinds.
UPDATE. A more correct SQL-query, perhaps:
SELECT
*some way to sum cost*
FROM tournament AS t
JOIN attribute AS ta ON t.attr_id = ta.toaid
JOIN registration AS tr ON tr.tourneyId = t.tourneyId
INNER JOIN pokerstuff as ga ON ta.game_attr_id = ga.gameId
LEFT JOIN people AS p ON p.userId = tr.userId
LEFT JOIN parttaking AS jlt ON (jlt.tourneyId = t.tourneyId AND tr.userId = jlt.userId)
LEFT JOIN (
SELECT t.tourneyId,
ta.a - (ta.b) - sum(c)*ta.cost AS cost
FROM tournament as t
JOIN attribute as ta ON (t.attr_id = ta.toaid)
JOIN registration tr ON (tr.tourneyId = t.tourneyId)
GROUP BY t.tourneyId, ta.b, ta.a
) as o on t.tourneyId = o.tourneyId
AND whereConditions
GROUP BY groupBySql
Description of the tables
tournament (tourneyId, name, attributeId)
attributes (attributeId, ..., gameid)
registration (userId, tourneyId, ...)
pokerstuff(gameid,...)
people(userId,...)
parttaking(userId, tourneyId,...)
Let's assume that we have the following (cost is actually calculated in a subquery, but since it's tied to tournament, I will treat it as an attribute here):
tournament:
tourneyId | name | cost
1 | MyTournament | 50
2 | MyTournament | 80
and
userId | tourneyId
1 | 1
2 | 1
3 | 1
4 | 1
1 | 2
4 | 2
The problem is rather simple. I need to be able to get the sum of the costs of the tournaments without counting a tournament more than once. The sum (and all other aggregates) will be dynamically grouped by the user.
A big problem is that many solutions that I've tried (such as SUM OVER...) would require that I group by certain attributes, and that I cannot do. The group by-clause must be completely decided by the user. The sum of the cost should sum over any group-by attributes, the only problem is of course the multiple rows in which the sum appears.
Do anyone of you have any good hints on what can be done?
Try the following:
select *selected by user*, sum(case rownum when 1 then a.cost end)
from
(
select
*selected by user*, cost,
row_number() over (partition by t.tid) as rownum
FROM t
JOIN ta ON t.attr_id = ta.toaid
JOIN tr ON tr.toid = t.toid
) a
group by *selected by user*
The row_number is used to number each row with the same tournament row. When suming the costs we only consider those rows with a rownum of 1. All other rows are duplicates of this one with regards to the costs.
In terms of the fiddle:
select table_size, sum(case rownum when 1 then a.cost end)
from
(
SELECT
table_size, cost,
row_number() over (partition by t.tid) as rownum
FROM tournament as t
LEFT JOIN takes_part AS tp ON tp.tid = t.tid
LEFT JOIN users as u on u.uid = tp.tid
JOIN attributes as a on a.aid = t.attrId
) a
group by table_size
As the repeated costs are the same each time you can average them by their hidden id and do something like this:
WITH MrTable AS (
SELECT DISTINCT hidden_id, AVG(cost) OVER (PARTITION BY hidden_id) AS cost
FROM stuff
)
SELECT SUM(cost) FROM MrTable;
(Updated) Given that the cost currently returned is the total cost per tournament, you could include a fractional value of cost on each line of an inner select, such that the total of all those values adds up to the total cost (allowing for the fact that each given tournament's values may be appearing multiple times), then sum that fractional cost in your outer select, like so:
select table_size, sum(frac_cost) as agg_cost from
(SELECT a.table_size , cost / count(*) over (partition by t.tid) as frac_cost
FROM tournament as t
LEFT JOIN takes_part AS tp ON tp.tid = t.tid
LEFT JOIN users as u on u.uid = tp.uid
JOIN attributes as a on a.aid = t.attrId) sq
GROUP BY table_size
SQLFiddle here.

Beginner SQL query with ROW_NUMBER

i'm kind of a beginner with SQL.
Right now i'm trying to create a bit complex select but i'm getting some error, which I know it's a beginner mistake.
Any help appreciated.
SELECT ROW_NUMBER() OVER (ORDER BY score) AS rank, userID, facebookID, name, score FROM (
SELECT * FROM Friends AS FR WHERE userID = ?
JOIN
Users WHERE Users.facebookID = FR.facebookFriendID
)
UNION (
SELECT * FROM User WHERE userID = ?
)
Where the 2 ? will be replaced with my user's ID.
The table User contains every user in my db, while the Friends table contains all facebookFriends for a user.
USER TABLE
userID | facebookID | name | score
FRIENDS TABLE
userID | facebookFriendID
Sample data
USER
A | facebookID1 | Alex | 100
B | facebookID2 | Mike | 200
FRIENDS
A | facebookID2
A | facebookID3
B | facebookID1
I'd like this result since Alex and mike are friends:
rank | userID | facebookID | name
1 | B | facebookID2 | Mike
2 | A | facebookID1 | Alex
I hope this was quite clear explanation.
I'm getting this error at the moment:
Error occurred executing query: Incorrect syntax near the keyword 'AS'.
You've got several issues with your query. JOINS come before WHERE clauses. And when using a JOIN, you need to specify your ON clauses. Also when using a UNION, you need to make sure the same number of fields are returned in both queries.
Give this a try:
SELECT ROW_NUMBER() OVER (ORDER BY score) AS rank, userID, facebookID, name, score
FROM (
SELECT *
FROM Users
WHERE UserId = 'A'
UNION
SELECT U.userId, u.facebookId, u.name, u.score
FROM Friends FR
JOIN Users U ON U.facebookID = FR.facebookFriendID
WHERE FR.userID = 'A' ) t
SQL Fiddle Demo
Also, by the way your using ROW_NUMBER, it really will be a Row Number vs a RANK. If you want Rankings (with potential ties), replace ROW_NUMBER with RANK.