SQL Data Rows as Columns - sql

I am wanting to return columns representing various users for a CSV file for part of our database.
The base tables look something like this:
ProviderId ProviderName
1 Test
2 FooBar
UserId UserName
1 Mike
2 Bob
3 John
ProviderId UserId
1 1
2 2
1 3
I want the results to look like this:
ProviderName UserName1 UserName2 ...
Test Mike John
FooBar Bob
So far, I tried using pivot for this, but it only supports Ints (and thus only the Ids):
SELECT ProviderId, [1],[2],[3],[4] FROM (
SELECT p.ProviderId, u.UserId, ROW_NUMBER() OVER(PARTITION BY p.ProviderId ORDER BY p.ProviderId ASC, u.UserId ASC) as Row
FROM Provider p
left join ProviderUser pu on p.ProviderId=pu.ProviderId
left join [User] u on pu.UserId = u.UserId
) as MyTable
PIVOT
(
SUM(UserId) FOR Row IN ([1],[2],[3],[4])
) as PivotTable
ProviderId 1 2 3 4
1 2 15 18 22
2 17 23 NULL NULL
However, I need to be able to take the usernames (and other text details) out of user child rows and append them as columns per their provider parent.
Any suggestions for making this work?
Thanks

Ok, if you want to get a dynamic number of columns, then you are gonna have to use dynamic sql. For that, please take a look at this link first. Then, you can try this:
DECLARE #Users NVARCHAR(MAX), #Query NVARCHAR(MAX)
SET #Users = ''
SELECT #Users = #Users + '[UserName' + CAST(UserId AS VARCHAR) +'],'
FROM Users
ORDER BY UserId
SET #Users = LEFT(#Users,LEN(#Users)-1)
SET #Query = '
SELECT ProviderName, '+#Users+'
FROM ( SELECT B.ProviderName, C.UserName, ''UserName''+CAST(A.UserId AS VARCHAR) UserAlias
FROM ProviderUsers A
JOIN Provider B
ON A.ProviderId = B.ProviderId
JOIN Users C
ON A.UserId = C.UserId) T
PIVOT(MIN(UserName) FOR UserAlias IN ('+#Users+')) AS PT'
EXEC sp_executesql #Query

Related

Return Duplicate emails along with User Ids that are different

I'm running into an issue with a duplicate query and I hope you guys can help.
Essentially what I want to do is find and list of the duplicate emails associated with different userids
My query is:
select UserId, acitveid, email, userstatusid
from (select u.UserId, u.acitveid, cd.email, u.userstatusid,
count(*)over (partition by cd.email) as cnt
from ContactDetails cd
join UserContactDetails ucd on ucd.ContactDetailsId = cd.ContactDetailsId
join dbo.[User] u on u.UserId = ucd.UserId ) ua
where cnt >1
The issue I have with the above query is that it is returning the same userids for some of the results so it looks like:
Userid AcitveId email UserStatusid
123 1 abc#123.com 1
123 1 abc#123.com 1
135 1 efg#123.com 1
142 1 efg#123.com 1
The results Im looking for are simply:
Userid AcitveId email UserStatusid
135 1 efg#123.com 1
142 1 efg#123.com 1
WITH base AS (
SELECT DISTINCT u.UserId
,u.acitveid
,cd.email
,u.userstatusid
,
FROM ContactDetails cd
JOIN UserContactDetails ucd ON ucd.ContactDetailsId = cd.ContactDetailsId
JOIN dbo.[User] u ON u.UserId = ucd.UserId
)
,duplicate_emails AS (
SELECT email
,count(userId) AS cnt
FROM base
GROUP BY 1
HAVING count(userId) > 1
)
SELECT b.*
FROM base b
JOIN duplicate_emails de ON b.email = de.email
A self join across Email = email and id <> id would work fine here. That said, your request and lack of sample data means that we are largely guessing based off the query and sample output you have provided. The below should get you pretty close and, if you update your OP, I am sure we can get you exactly what you're after.
SELECT ActiveUser.UserID Active_UserID,
ActiveUser.ActiveID Active_ActiveID,
ContactDetails.email AS Email,
DuplicateUser.UserID AS Dup_UserID,
DuplicateUser.ActiveID As Dup_ActiveID
FROM ContactDetails INNER JOIN
ContactDetails AS Duplicate ON ContactDetails.email = Duplicate.email AND ContactDetails.UserID <> Duplicate.UserID INNER JOIN
UserContactDetails AS ActiveUserContactDetails ON ActiveUserContactDetails.ContactDetailsID = ContactDetails.ContactDetailsID INNER JOIN
dbo.[User] AS ActiveUser ON ActiveUser.UserID = ActiveUserContactDetails.UserID INNER JOIN
UserContactDetails AS DuplicateUserContactDetails ON DuplicateUserContactDetails.ContactDetailsID = Duplicate.ContactDetailsID INNER JOIN
dbo.[User] AS DuplicateUser ON DuplicateUser.UserID = UserContactDetails.UserID

Subquery SQL for link name and firstname

Hello I would like to retrieve the name and the first name in the user table thanks to the id contained in the message table (id_receive and id_send) in sql via a subquery
SELECT user.nom FROM user
WHERE user.id IN (
SELECT message.id_send, message.id_receive FROM message WHERE message.id=1
)
```
I would recommend using EXISTS, twice:
SELECT u.nom
FROM user u
WHERE EXISTS (SELECT 1 FROM message m WHERE m.id = 1 AND u.id = id_send) OR
EXISTS (SELECT 1 FROM message m WHERE m.id = 1 AND u.id = id_receive) ;
However, a JOIN might also be appropriate:
SELECT u.nom
FROM user u JOIN
message m
ON u.id IN (m.id_send, id_receive)
WHERE m.id = 1;
I suspect it isn't actually what you want but it looks like this is what you're trying to do:
SELECT user.nom FROM user
WHERE user.id IN (
SELECT message.id_send FROM message WHERE message.id=1
UNION ALL
SELECT message.id_receive FROM message WHERE message.id=1
)
The query that drives the IN should return a single column of values
Try and conceive that in works like this:
SELECT * FROM t WHERE c IN(
1
2
3
)
Not like this:
SELECT * FROM t WHERE c IN(
1 2 3
)
Nor like this:
SELECT * FROM t WHERE c IN(
1 2 3
4 5 6
)
It might help you reember that the query inside it must return a single column, but multiple rows, all of qhich are searched for a matching value c by IN
Small addition to your original query to make it working:
SELECT user.nom FROM user
WHERE user.id IN (
SELECT unnest(array[message.id_send, message.id_receive])
FROM message
WHERE message.id=1
)

SQL SUM() by value of another column

I am creating a generic key/value data store in MS SQL Server. I have a UserDecimalData table defined as follows
create table [UserDecimalData] (
[UserID] nvarchar(64) not null,
[ValueKey] uniqueidentifier not null,
[Value] decimal null
);
alter table [UserDecimalData]
add constraint PK_UserDecimalData primary key clustered ([UserID], [Date], [ValueKey]);
and as an example, here is some data that uses a GUID ending with 0001 to indicate an individual's score in a game, and a GUID ending with 0002 to indicate which team a user is on.
UserID ValueKey Value
Dave 00000000-0000-0000-0000-000000000001 35
Dave 00000000-0000-0000-0000-000000000002 1
Phil 00000000-0000-0000-0000-000000000001 35
Phil 00000000-0000-0000-0000-000000000002 1
Pete 00000000-0000-0000-0000-000000000001 35
Pete 00000000-0000-0000-0000-000000000002 2
I can easily find the score for an individual by searching for their UserID + ValueKey ending with 0001. I can also find out which team an individual is in by searching for their UserID + the value of the row where the ValueKey ends with 0002.
My question is, how can I get a list of team scores? In this case team 1 would have 70 and team 2 would have 35.
Here is one method:
select sum(udd.value)
from UserDecimalData udd
where udd.ValueKey like '%1' and
exists (select 1
from UserDecimalData udd2
where udd2.ValueKey like '%2' and
udd2.UserId = udd.UserId
);
select v1.Value as Team, sum(v2.value) as TeamScore
from userdecimaldata v1
join userdecimaldata v2 on v2.UserId = v1.UserId and v2.[Date] = v1.[Date]
where v1.ValueKey = #clankey and v2.ValueKey = #scoreKey
group by v1.Value
OUTPUT
Team TeamSccore
1 70
2 35
--first way
with commands as (
select UserID as UserID, Value as command
from UserDecimalData
where ValueKey like '%2'
)
select c.command, sum(Value)
from UserDecimalData u
left join commands c on u.UserID = c.UserID
where ValueKey like '%1'
group by command
GO
--second way
select c.command, sum(Value)
from UserDecimalData u
left join (
select UserID as UserID, Value as command
from UserDecimalData
where ValueKey like '%2'
) c on u.UserID = c.UserID
where ValueKey like '%1'
group by command
GO
--results
--1 70
--2 35
select u1.Value, sum(u0.Value)
from UserDecimalData u1
left join UserDecimalData u0 on u0.UserID = u1.UserID and u0.ValueKey like '%1'
where u1.ValueKey like '%2'
group by u1.Value
GO

how to update a column with multiple values except particular values

tUser table is having values
UserID UserName Email isActive
---------------------------------------------
1 abc abc#gmail.com 1
2 abc abc#gmail.com 1
3 abc abc#gmail.com 1
4 abc abc#gmail.com 1
22 cd cd#gmail.com 1
23 cd cd#gmail.com 1
24 ef ef#gmail.com 1
25 ef ef#gmail.com 1
The values are duplicate of UserName abc.
I have a query to update isActive column of UserName 'abc'.
UPDATE dbo.tUser
SET isActive = 0
FROM Duplicates d,dbo.tUser
WHERE d.UserId =dbo.tUser.UserId
and d.RN>1
But it is updating the first column of UserName 'abc'. I need to keep a particular userID '4' as 1 and other values to update with 0. How can i do it?
If i need to include 4,23 and 25. How can i do it?
i tried with
;WITH Duplicates AS
(
SELECT [UserId],[UserName],[Email],
RN = ROW_NUMBER()OVER(PARTITION BY[UserName],[Email] ORDER BY [UserName],[Email])
from dbo.tUser
)
UPDATE U
SET isActive = (case when U.UserId IN (4,23,25) THEN 1 ELSE 0 END)
FROM Duplicates d
JOIN dbo.tUser U
ON d.UserId =U.UserId
and d.RN>1
But it is not working
If I understand the question correctly, you can just use a CASE expression:
UPDATE u
SET isActive = (CASE WHEN u.UserId = 4 THEN 1 ELSE 0 END)
FROM Duplicates d JOIN
dbo.tUser u
ON d.UserId = u.UserId
WHERE d.RN > 1;
Note: Learn to use proper explicit JOIN syntax. Simple rule: Never use commas in the FROM clause.
First of all what is d.RN? What is the structure of the Duplicates table? Is it possible that Duplicates.UserId may contain an Id that is similar to tUser.UserId yet refer to a totally different record?
The following script will work for you (however it's not fully tested as I don't have an IDE and SQLFiddler seem to be down:
UPDATE u
SET u.IsActive = 0
FROM dbo.tUser u
LEFT JOIN (SELECT UserId, RANK() OVER (Partition [UserName], [Email] ORDER BY [UserName], [Email]) AS RNK) d
ON d.UserId = tUser.UserId
WHERE d.RNK > 1

select distinct from join

My Tables look like this
Table 1 Table 2
Users Options
id name id user_id option
------- --- -------- --------
1 Donald 1 1 access1
2 John 2 1 access2
3 Bruce 3 1 access3
4 Paul 4 2 access1
5 Ronald 5 2 access3
6 Steve 6 3 access1
Now, i want to select join these to find a user which has only access1
If i do something like
select t1.id,t1.name,t2.id,t2.user_id,t2.option
from table1 t1, table2 t2
where t1.id=t2.user_id
and option='access1';
This does not give me unique results, as in the example i need only user_id=3 my data has has these in hundreds
I also tried something like
select user_id from table2 where option='access1'
and user_id not in (select user_id from table2 where option<>'access1')
There have been other unsuccessful attempts too but i am stuck here
You can do this using a EXISTS subquery (technically, a left semijoin):
SELECT id, name
FROM table1
WHERE EXISTS(
SELECT * FROM table2
WHERE table1.id = table2.user_id
AND table2.option = 'access1'
)
If you want only users that have access1 and not any other access, add NOT EXISTS (a left anti-semi-join; there's a term to impress your colleagues!):
AND NOT EXISTS (
SELECT * FROM table2
WHERE table1.id = table2.user_id
AND table2.option <> 'access1'
)
bool_and makes it simple
with users (id,name) as ( values
(1,'donald'),
(2,'john'),
(3,'bruce'),
(4,'paul'),
(5,'ronald'),
(6,'steve')
), options (id,user_id,option) as ( values
(1,1,'access1'),
(2,1,'access2'),
(3,1,'access3'),
(4,2,'access1'),
(5,2,'access3'),
(6,3,'access1')
)
select u.id, u.name
from
users u
inner join
options o on o.user_id = u.id
group by 1, 2
having bool_and(o.option = 'access1')
;
id | name
----+-------
3 | bruce
If you want the user that has only access1, I would use aggregation:
select user_id
from table2
group by user_id
having min(option) = max(option) and min(option) = 'access1';
WITH users(id,name) AS ( VALUES
(1,'Donald'),
(2,'John'),
(3,'Bruce'),
(4,'Paul'),
(5,'Ronald'),
(6,'Steve')
), options(id,user_id,option) AS ( VALUES
(1,1,'access1'),
(2,1,'access2'),
(3,1,'access3'),
(4,2,'access1'),
(5,2,'access3'),
(6,3,'access1')
), user_access_count AS (
SELECT op.user_id,count(op.option) AS access_count
FROM options op
WHERE EXISTS(
SELECT 1 FROM options
WHERE option = 'access1'
)
GROUP BY op.user_id
)
SELECT u.id,u.name
FROM users u
INNER JOIN user_access_count uac ON uac.user_id = u.id
WHERE uac.access_count = 1;