Selecting row data as columns - mysql

I have a contacts table where contact_type_id means:
1- email2 - phone3 - skype
The example below shows 3 different users with different types of contacts.
First user has phone number and Skype. The second one has only email. The third one has all 3 types: email, phone number & Skype.
user_id contact_type_id value
1 2 353234
1 3 skypeLogin
2 1 example#mail.com
3 1 example2#mail.com
3 2 123345
3 3 skypeLogin2
Issue needed to be clarified
How can I select this data as the following table
user_id email phone skype
1 null 353234 skypeLogin
2 example#mail.com null null
3 example2#mail.com 123345 skypeLogin2

Pivot is what you're looking for. If you have an unknown number of contact_type_id's then google Dynamic Pivot - there are tons of examples on SO.
This is for SQL Server:
CREATE TABLE #Contacts
(
user_id INTEGER
,contact_type_id INTEGER
,value NVARCHAR(20)
);
INSERT INTO #Contacts
( user_id, contact_type_id, value )
VALUES
( 1, 2, '353234' ),
( 1, 3, 'skypeLogin' ),
( 2, 1, 'example#mail.com' ),
( 3, 1, 'example2#mail.com' ),
( 3, 2, '123345' ),
( 3, 3, 'skypeLogin2' );
SELECT
pvt.user_id
,pvt.[1] email
,pvt.[2] phone
,pvt.[3] skype
FROM
#Contacts
PIVOT( MAX(value) FOR contact_type_id IN ( [1], [2], [3] ) ) pvt;
user_id email phone skype
----------- -------------------- -------------------- --------------------
1 NULL 353234 skypeLogin
2 example#mail.com NULL NULL
3 example2#mail.com 123345 skypeLogin2
(3 row(s) affected)

If MySQL then use CASE WHEN along with GROUP BY
SELECT
user_id,
MAX(CASE WHEN contact_type_id = 1 THEN `value` END) AS email,
MAX(CASE WHEN contact_type_id = 2 THEN `value` END) AS phone,
MAX(CASE WHEN contact_type_id = 3 THEN `value` END) AS skype
FROM your_table
GROUP BY user_id
WORKING DEMO

This works on sql server and mysql. Note value should be escaped in sql server.
USE sandbox;
/*
create table users(user_id int,contact_type_id int, value varchar(20));
insert into users values
(1, 2, '353234'),
(1, 3, 'skypeLogin'),
(2, 1, 'example#mail.com'),
(3, 1, 'example2#mail.com'),
(3, 2, '123345'),
(3, 3, 'skypeLogin2');
*/
select user_id,
max(case when contact_type_id = 1 then value else '' end) as email,
max(case when contact_type_id = 2 then value else '' end) as tel,
max(case when contact_type_id = 3 then value else '' end) as skpe
from users
group by user_id;

One is Pivot table, other can be below logic:
Create 3 Tables :
1. With user_id and Value for only email i.e. where contact_type_id=1
2. With user_id and Value for only phone i.e. where contact_type_id=2
3. With user_id and Value for only skype i.e. where contact_type_id=3
4. Outer join the three tables.
Below code does the same:
Select A.user_id,A.value as email, B.value as Phone, C.Value as Skype
from contacts A
outer join contacts B
On A.User_id=B.User_ID and A.value=1 and B.value=2
outer join contacts C
On A.User_id=C.User_ID and C.Value=3

Related

MySQL Query over multiple tables in the given example

I'd like to receive a table from my database but I am unable to form the query.
This is what I like to achieve: Think of a group of users who shall be invited to an event.
To find a date A list of dates are provided by the host.
So far we have these tables:
Users:
Id
Name
7
Sally
2
Bob
3
John
4
Lisa
5
Joe
6
Jane
Events
Id
Name
1
Birthdayparty
2
Barbecue
3
Dinner
Event Users:
Id
UserId
EventId
1
7
1
(Sally is invited to bp)
2
2
1
(Bob, too)
3
4
1
(and Lisa)
4
1
2
(Sally is invited. to Bbe)
5
5
2
(also Joe)
6
4
2
(and Lisa)
So far for the structure of main parts of the db.
Now lets put some possible dates for the birthday party
EventProposal:
Id
Event
Date
1
1
5. March
Birthday dates
2
1
6. March
3
1
8. March
4
1
10. March
5
3
4. April
Dinner
6
3
5. April
Now the last table holds which user selected which dates for an event.
EventProposalSelection:
Id
EventId
UserId
DateId
1
1
1
1
Sally selected 5. March for birthday
2
1
1
2
Sally selected 6. March for birthday
3
1
1
3
Sally selected 8. March for birthday
4
1
2
2
Bob selected 6. March for birthday
5
1
2
3
Bob selected 8. March for birthday
6
1
4
1
Lisa selected 5. March for birthday
7
1
4
2
Lisa selected 6. March for birthday
8
1
4
4
Lisa selected 10. March for birthday
What I like to know is if a user has picked a date for an event.
I want to see all dates of a specific event for a specific user
(where clause contains userId and eventId)
If I ask for Sally in combination of Birthday
(where userId = 1 and eventId = 1)
I'd like to receive
DateId
Checked
1
true
2
true
3
false
4
true
The tables are properly constrained and related to each other
How can I achieve this in MySQL?
EDIT:
select
e.Name EventName,
e.id,
dp.DateProposal DateOfEvent,
coalesce( u.Name, '' ) GuestName,
-- due to left-join, see if the date is chosen or NULL (not chosen)
case when dps.dateid IS NULL then false else true end DateWasPicked
from
-- start with events
Event e
-- Now, what dates were birthday parties POSSIBLE to attend
JOIN EventProposal dp
on e.id = dp.EventId
-- NOW, what dates by given users were selected
-- BUT, since not all dates may be selected, do a LEFT JOIN
LEFT JOIN EventProposalSelection dps
on dp.id = dps.dateid
-- and finally who selected the given date
-- and again LEFT-JOIN since a user may not pick all dates
LEFT JOIN User u
on dps.userid = u.id
-- prevent getting ALL users for an event
AND u.Id = 7 --Sally
where
-- but only birthday parties
e.Id = 1
-- Tricky here because you want ALL POSSIBLE event dates,
-- but ONLY those for Sally
AND ( dps.dateid IS NULL OR u.Id = 7 )
order by
-- and suggest you actually use datetime based column
-- as you can use functions to get the format of the date.
dp.DateProposal
Lead to
which seems to be fine, but when running for Bob (UserId = 2)
there is a date missing
And running for John (UserId = 3)
Ok, so lets take this one step at a time. You are interested in a SPECIFIC event, and all POSSIBLE dates FOR said event. Then, based on a specific user, if they had (or not) picked any of the possible dates. And by the user ID, get the name too.
Sample data. Your version of data had the sample selections with Sally's ID of 1, not 7. So this is a sample set I ran with using 7 as the basis for Sally
create table users ( id integer, name varchar(10))
insert into users ( id, name ) values
( 7, 'Sally' ),
( 2, 'Bob' ),
( 3, 'John' ),
( 4, 'Lisa' ),
( 5, 'Joe' ),
( 6, 'Jane' )
create table Events ( id int, name varchar(15))
insert into Events (id, name ) values
( 1, 'Birthdayparty' ),
( 2, 'BBQ' ),
( 3, 'Dinner' )
create table EventUsers ( id int, userid int, eventid int )
insert into EventUsers ( id, userid, eventid ) values
( 1, 7, 1 ),
( 2, 2, 1 ),
( 3, 4, 1 ),
( 4, 1, 2 ),
( 5, 5, 2 ),
( 6, 4, 2 )
create table EventProposal (id int, event int, date datetime )
insert into EventProposal ( id, event, date ) values
( 1, 1, '2022-03-05' ),
( 2, 1, '2022-03-06' ),
( 3, 1, '2022-03-08' ),
( 4, 1, '2022-03-10' ),
( 5, 3, '2022-04-04' ),
( 6, 3, '2022-04-05' )
create table EventProposalSelection ( id int, eventid int, userid int, DateID int )
insert into EventProposalSelection ( id, eventid, userid, dateid ) values
( 1, 1, 7, 1 ),
( 2, 1, 7, 2 ),
( 3, 1, 7, 3 ),
( 4, 1, 2, 2 ),
( 5, 1, 2, 3 ),
( 6, 1, 4, 1 ),
( 7, 1, 4, 2 ),
( 8, 1, 4, 4 )
select
AllEventDates.id,
AllEventDates.EventName,
AllEventDates.DateOfEvent,
u.id UserID,
coalesce( u.Name, '' ) GuestName,
-- due to left-join, see if the date is chosen or NULL (not chosen)
case when eps.dateid IS NULL
then 'false' else 'true' end DateWasPicked
from
Users u
-- this query get all events and all possible dates regardless
-- of who may have supplied a selection to attend
JOIN
( select
e.id,
e.Name EventName,
ep.id EventProposalID,
ep.date DateOfEvent
from
Events e
JOIN EventProposal ep
on e.id = ep.Event
where
-- but only birthday parties
e.Name = 'Birthdayparty' ) AllEventDates
on 1=1
-- NOW, left join for a given one person
LEFT JOIN EventProposalSelection eps
on eps.userid = u.id
AND AllEventDates.EventProposalID = eps.dateid
-- and finally who selected the given date
-- and again LEFT-JOIN since a user may not pick all dates
where
u.id = 7
order by
-- and suggest you actually use datetime based column
-- as you can use functions to get the format of the date.
AllEventDates.DateOfEvent

sql get min of subgroup within group for each subgroup on 1 row

I am trying to find the first message of a subgroup, for all subgroups. The result should be on one single row.
Here is an example schema.
CREATE TABLE test
(`name` varchar(10), `message_id` int(10), `timing` datetime)
;
INSERT INTO test
VALUES
('John', 1, '2018-07-02 2:09:01'),
('Peter', 1, '2018-07-02 2:08:54'),
('John', 1, '2018-07-02 2:09:04'),
('Peter', 2, '2018-07-02 2:09:09')
;
http://www.sqlfiddle.com/#!9/56e480
I can manage to get the first message of each subgroup in two seperate queries, but not in a single one:
select distinct message_id, min(timing) as first_message_peter from test
where name = 'Peter'
group by 1
Resulting in:
message_id| first_message_peter
1 | 2018-07-02 02:08:54
2 | 2018-07-02 02:09:09
What I would like is the following:
message_id| first_message_peter | first_message_john
1 | 2018-07-02 02:08:54 | 2018-07-02 02:09:01
2 | 2018-07-02 02:09:09 | NULL
Could someone help me in the right direction?
You can do conditional aggregation for this:
select
message_id,
min(case when name = 'Peter' then timing end) first_message_peter ,
min(case when name = 'John' then timing end) first_message_john
from test
where name in ('Peter', 'John')
group by message_id
Updated demo on DB Fiddle

How can I count data in a single table, and combine with data from multiple tables?

I have 3 tables:
users - with an id, client_id and username
maindata - with a client_id, usernames, and descriptions (many usernames for each client_id) and the main data that I need to COUNT.
clients - with a client_id and client_name (not a username)
I need to grab the data from maindata which has fields of:
usernames, client_id, and description
and see for all records in maindata: how many appear with the same username, client_id and description.
Once I get the count, I need to grab the user_id associated with that username from maindata.
I feel like I could do this in one SQL call, but for now I am running a for loop after getting a list of all users (because that has the usernames and client_id), but not sure I need to, I may be able to include those in my query.
cur.execute("SELECT * FROM users")
users = cur.fetchall()
for u in users:
user = u[2]
client_id = u[1]
cur.execute("SELECT clients.name,maindata.client_id,maindata.username,users.id,COUNT(*) "
"FROM maindata CROSS JOIN users "
"INNER JOIN clients ON maindata.client_id=clients.id "
"WHERE description LIKE '%teal%' "
"AND maindata.username='{}' AND maindata.client_id='{}' ".format(user,client_id)) #This will return the client and the number of countableDatas
totalCountswithIDs = cur.fetchall()
So the end result should be return value of:
Client Name (found in clients)
Client ID (found in maindata)
Username (found in maindata)
User ID (found in users)
Count (for all combinations of Client ID + Username + specified description)
Am I far off? Thank you for any help in advance!
Sample data:
maindata:
id, client_id, username, description
(1, '1', 'rusty', 'blue'),
(2, '2', 'john', 'yellow brick road'),
(3, '3', 'helen', 'teal'),
(4, '3', 'helen', 'teal'),
(5, '3', 'helen', 'teal'),
users:
id, client_id, username
(1743, 2, 'john'),
(1742, 3, 'helen'),
(1189, 1, 'rusty'),
clients:
id, name
(1, 'Apple'),
(2, 'Amazon'),
(3, 'Google'),
The results from this would be:
Apple, 1, rusty, 1189, 1
Amazon, 2, john, 1743, 1
Google, 3, helen, 1742, 3
The last one has 3 because there are 3 that match my LIKE search of "teal", as an example.
If I understood you question correctly this would be one approach
with users as (
Select 1743 id, 2 client_id ,'john' username UNION ALL
Select 1742 id, 3 client_id ,'helen' username UNION ALL
Select 1189 id, 1 client_id ,'rusty' username
)
,
maindata as
(
SELECT 1 id , '1' client_id, 'apple' username , 1520900834 DontKnown, 'blue' description UNION ALL
SELECT 2, '2', 'admin', 1520901427, 'yellow brick road' UNION ALL
SELECT 3, '3', 'helen', 1520902247, 'teal' UNION ALL
SELECT 4, '3', 'helen', 1520902243, 'teal' UNION ALL
SELECT 5, '3', 'helen', 15202022347, 'teal'
),
clients as
(Select 1 client_id ,'Apple' name union all
Select 2,'Amazon' UNION ALL
Select 3,'Google'
) --Apple, 1, rusty, 1189, 1 Amazon, 2, john, 1743, 1 Google, 3, helen, 1742, 3
select distinct c.name,c.client_id,u.username,u.id,m.cnt_maindata
FROM
(
Select *,count(*) OVER(PARTITION BY client_id,description) cnt_maindata from maindata
) m
JOIN users u on m.client_id=u.client_id
JOIN clients c on c.client_id=m.client_id
Output:
name client_id username id cnt_maindata
Amazon 2 john 1743 1
Apple 1 rusty 1189 1
Google 3 helen 1742 3

SQL - Get boolean values for each matching field

I need help with SQL request.
I have 3 tables:
Table User
id name
1 Jon
2 Jack
3 Bill
Table Type
id name
1 View
2 Edit
3 Delete
Table Right
id user type
1 1 1
2 1 2
3 1 3
4 2 1
5 3 1
So table Right contains linked pairs of user-type. I need a request which gets user name, and a boolean (BIT) value for each enrty in table Type, which exists in Right table for this user. Something like this for my example tables:
Username View Edit Delete
Jon 1 1 1
Jack 1 0 0
Bill 1 0 0
Thank you very much in advance!
untested:
select name,
coalesce(select 1 from `right` where `type` = 1 and right.user = user.id, 0) as `View`,
coalesce(select 1 from `right` where `type` = 2 and right.user = user.id, 0) as `Edit`,
coalesce(select 1 from `right` where `type` = 3 and right.user = user.id, 0) as `Delete`
from User
Alternatively:
select name, coalesce(RVIEW.R, 0) as `View`, coalesce(REDIT.R, 0) as `Edit`, coalesce(RDEL.R, 0) as `Delete`
from User
left join (select 1 R from `right` where `type` = 1) RVIEW on (right.user = user.id)
left join (select 1 R from `right` where `type` = 2) REDIT on (right.user = user.id)
left join (select 1 R from `right` where `type` = 3) RDEL on (right.user = user.id)
In your example, you are using reserved words as table names.
If you want to learn more about naming conventions for table names, have a look at the links in an earlier question on Stack Overflow here
Example below shows yet another way of getting the data you want (with other names for the tables):
select person.name as Username
, max( if( person_right.type_id = 1, 1, 0 ) ) as `View`
, max( if( person_right.type_id = 2, 1, 0 ) ) as `Edit`
, max( if( person_right.type_id = 3, 1, 0 ) ) as `Delete`
from person
left outer join person_right
on person_right.user_id = person.id
group by person.name
order by person.id
Another thing that might be worth looking at is the datamodel,
because Rights are normally quite "fixed".
If anyone accidentally changes one of the names in the Type table, you might have a serious security issue.
What you can do is change the person_right table to look like this
windowid user_id view_access edit_access delete_access
1 1 1 1 1
1 2 1 0 0
1 3 1 0 0
where the primary key would be window_id+user_id allowing you to setup different rights per user in a particular window/part of your application.
Hope this helps.

MySQL - loop through rows

I have the following code
select count(*)
from (select Annotations.user_id
from Annotations, Users
where Users.gender = 'Female'
and Users.user_id = Annotations.user_id
and image_id = 1
group by Annotations.user_id
having sum(case when stem = 'taxi' then 1 else 0 end) > 0 and
sum(case when stem = 'zebra crossing' then 1 else 0 end) > 0
) Annotations
It produces a count of how many females who have given the stem 'taxi' and 'zebra crossing' for image 1.
Sample data
user id, image id, stem
1 1 image
1 1 taxi
1 1 zebra crossing
2 1 person
2 1 zebra crossing
2 1 taxi
3 1 person
3 1 zebra crossing
Expected result (or similar)
stem1, stem2, count
taxi , zebra crossing 2
person, zebra crossing 2
However, as there are over 2000 stems, I cannot specify them all.
How would I go around looping through the stem rows with the image_id = 1 and gender = female as opposed to specifying the stem string?
Thank you
As per my understanding, you need to fetch female users that have 2 or more stems
Update: It seems you need to display the user's that have a stem that is used by another user too, I have updated the query for the same
SELECT
distinct a.user_id,
group_concat(DISTINCT a.stem ORDER BY a.stem)
FROM
Annotations a
JOIN Users u ON ( a.user_id = u.user_id AND u.gender = 'Female' )
JOIN
(
SELECT
b.user_id,
b.stem
FROM
Annotations b
) AS b ON ( a.user_id <> b.user_id AND b.stem = a.stem )
WHERE
a.image_id = 1
GROUP BY
a.user_id
UPDATE: As I understand it, you want to select all combinations of 2 stems, and get a count of how many users have that combination of stems. Here is my solution:
SELECT stem1, stem2, count(*) as count FROM
(
SELECT a.user_id,a.image_id,a.stem as stem1,b.stem as stem2
FROM Annotations a JOIN Annotations b
ON a.user_id=b.user_id && b.image_id=a.image_id && a.stem!=b.stem
JOIN Users ON Users.user_id = a.user_id
WHERE Users.gender = "Female"
) as stems GROUP BY stem1, stem2 having count > 1 WHERE image_id=1;
The caveat here is that it will return 2 rows for each combinations of stems. (The second occurrence will have the stems in reverse order).
Here's my attempt to solve your problem:
SELECT COUNT(*) AS Count, a1.stem AS Stem1, a2.Stem AS Stem2
FROM Annotations AS a1
INNER JOIN Annotations AS a2 ON a1.user_id = a2.user_id AND a1.image_id = a2.image_id
AND a1.stem < a2.stem
WHERE a1.image_id = 1
GROUP BY a1.stem, a2.Stem
HAVING COUNT(*) > 1;
I did not include image_id logic.
Please see my SQL Fiddle here: http://sqlfiddle.com/#!2/4ee69/33
Based on the following data (copied from yours) I get the result posted underneath it.
CREATE TABLE Annotations
(`user_id` int, `image_id` int, `stem` varchar(14))
;
INSERT INTO Annotations
(`user_id`, `image_id`, `stem`)
VALUES
(1, 1, 'image'),
(1, 1, 'taxi'),
(1, 1, 'zebra crossing'),
(2, 1, 'person'),
(2, 1, 'zebra crossing'),
(2, 1, 'taxi'),
(3, 1, 'person'),
(3, 1, 'zebra crossing')
;
COUNT STEM1 STEM2
2 person zebra crossing
2 taxi zebra crossing