Using IN clause with List agg. function - sybase

is there a way to use "IN" clause with the "List" aggregate function, like in this example:
SELECT
FirstName,
LastName,
CASE
WHEN 1 IN LIST(ID) THEN 'Admin'
WHEN 2 IN LIST(ID) THEN 'Moderator'
WHEN 3 IN LIST(ID) THEN 'Owner'
ELSE
String(FirstName, ' ', LastName)
END as Description
FROM Users
Group By FirstName, LastName;
and if not what is the work around for that?

If I uderstant your query corectly, this should net the result you want:
SELECT
FirstName,
LastName,
CASE MIN(ID)
WHEN 1 THEN 'Admin'
WHEN 2 THEN 'Moderator'
WHEN 3 THEN 'Owner'
ELSE
String(FirstName, ' ', LastName)
END as Description
FROM Users
Group By FirstName, LastName;
Alternative but not pretty workaround could be CASE WHEN ','+LIST(ID)+',' LIKE '%,1,%' THEN ...

The only solution I found is to split the list again using sa_split_list like this:
SELECT
FirstName,
LastName,
LIST(ID) as lst,
CASE
WHEN 1 IN (SELECT row_value from sa_split_list(lst)) THEN 'Admin'
WHEN 2 IN (SELECT row_value from sa_split_list(lst)) THEN 'Moderator'
WHEN 3 IN (SELECT row_value from sa_split_list(lst)) THEN 'Owner'
ELSE
String(FirstName, ' ', LastName)
END as Description
FROM Users
Group By FirstName, LastName;
or
SELECT
FirstName,
LastName,
LIST(ID) as lst,
CASE
WHEN Exists(SELECT 1 from sa_split_list(lst) where row_value = 1) THEN 'Admin'
WHEN Exists(SELECT 1 from sa_split_list(lst) where row_value = 2) THEN 'Moderator'
WHEN Exists(SELECT 1 from sa_split_list(lst) where row_value = 3) THEN 'Owner'
ELSE
String(FirstName, ' ', LastName)
END as Description
FROM Users
Group By FirstName, LastName;
and that did the job, i still think the performance will be bad for big queries...

Related

More efficient way to write this T-SQL query? Pass column from EXISTS statement?

Is there a better way to write this? Primarily I'm referring to the THEN statement where I basically repeat the CASE WHEN EXISTS statement. It would be great to SELECT DateJoined inside the EXISTS statement and then use it outside that statement in the THEN. Is that possible?
SELECT
Email,
CASE
WHEN EXISTS (SELECT 1
FROM _Subscribers
WHERE EmailAddress = Email)
THEN (SELECT DateJoined
FROM _Subscribers
WHERE EmailAddress = Email)
ELSE LastModifiedDate
END as CreatedDate
FROM
[Optin Monster]
Thanks to #Lamak for the help on this one. This did the trick.
SELECT
om. Email,
CASE WHEN s.DateJoined <> '' OR s.DateJoined IS NOT NULL
THEN s.DateJoined
ELSE LastModifiedDate
END as CreatedDate
FROM [Optin Monster] om
LEFT JOIN _Subscribers s
ON om.Email = s.EmailAddress

Remove Comma from Case Statement

I realise this is a common question, but I'm wanting to avoid having to repeat the CASE statements multiple times.
Can anyone see a way of how the "," can be excluded from the string if only a few Cases are true(1)?
SELECT CONCAT
(
CASE WHEN Home = 1 THEN 'Own Home, ' END,
CASE WHEN Car = 1 THEN 'Car, ' END,
CASE WHEN Friend = 1 THEN 'Someone else''s home, ' END,
CASE WHEN Toilet = 1 THEN 'Toilet, ' END,
CASE WHEN Street = 1 THEN 'Street, park or beach, ' END,
CASE WHEN Squat = 1 THEN 'Squat, ' END,
CASE WHEN Other = 1 THEN 'Other' END
)
as 'Question 6',
from table
Try this
SELECT REVERSE(stuff(reverse(CONCAT
(
CASE WHEN Home = 1 THEN 'Own Home, ' END,
CASE WHEN Car = 1 THEN 'Car, ' END,
CASE WHEN Friend = 1 THEN 'Someone else''s home, ' END,
CASE WHEN Toilet = 1 THEN 'Toilet, ' END,
CASE WHEN Street = 1 THEN 'Street, park or beach, ' END,
CASE WHEN Squat = 1 THEN 'Squat, ' END,
CASE WHEN Other = 1 THEN 'Other' END
)),1 ,1,''))
as 'Question 6',
from TABLE
OR this
SELECT STUFF(CONCAT
(
CASE WHEN Home = 1 THEN ', Own Home' END,
CASE WHEN Car = 1 THEN ', Car' END,
CASE WHEN Friend = 1 THEN ', Someone else''s home' END,
CASE WHEN Toilet = 1 THEN ', Toilet' END,
CASE WHEN Street = 1 THEN ', Street, park or beach' END,
CASE WHEN Squat = 1 THEN ', Squat' END,
CASE WHEN Other = 1 THEN ', Other' END
),1,2,'')
as 'Question 6',
from table
It may not be the fastest, but just for fun.
WITH T3 AS
(
SELECT T1.PK, COALESCE(T2.Description, unpvt.Place) AS Description
FROM table AS T1
UNPIVOT
(Flag FOR Place IN (Home, Car, Friend, Toilet, Street, Squat, Other)
) AS unpvt
LEFT OUTER JOIN
(
SELECT *
FROM (VALUES('Home', 'Own Home'), ('Friend', 'Someone else''s home'), ('Street', 'Street, park or beach')) AS T(Place, Description)
) AS T2 ON unpvt.Place = T2.Place
WHERE Flag = 1
)
SELECT PK, (STUFF((
SELECT ', ' + Description
FROM T3 AS T4
WHERE T4.PK = T3.PK
FOR XML PATH('')), 1, 2, '')) AS Question6
FROM T3
GROUP BY PK
To preserve order in question, just add an order column to T2 above.
With SQL Server 2017, it's simpler.
SELECT T1.PK, STRING_AGG(COALESCE(T2.Description, unpvt.Place), ', ') AS Description
FROM table AS T1
UNPIVOT
(Flag FOR Place IN (Home, Car, Friend, Toilet, Street, Squat, Other)
) AS unpvt
LEFT OUTER JOIN
(
SELECT *
FROM (VALUES('Home', 'Own Home'), ('Friend', 'Someone else''s home'), ('Street', 'Street, park or beach')) AS T(Place, Description)
) AS T2 ON unpvt.Place = T2.Place
WHERE Flag = 1
GROUP BY PK

Convert groups of multiple key-value rows to XML

I have a table called userInfo that has data similar to the following:
Id, Field, Value
---------------------
1, FirstName, John
1, LastName, Smith
1, Age, 25
1, Gender, Male
2, FirstName, Jane
2, LastName, Smythe
2, Age, 24
2, Gender, Female
What I need is some T-SQL that will produce a single row for each Id with the following structure:
Row:1
<FieldValues>
<FirstName>John</FirstName>
<LastName>Smith</LastName>
<Age>25</Age>
<Gender>Male</Gender>
</FieldValues>
Row:2
<FieldValues>
<FirstName>Jane</FirstName>
<LastName>Smythe</LastName>
<Age>24</Age>
<Gender>Female</Gender>
</FieldValues>
I have tried a couple of things to get this but can't get figure this out.
Edit:
The list of Fields I provided here (i.e. FirstName, LastName, etc) is not a static list of fields. I will be adding and taking away from this list all the time so the query would be able to handle this automatically). Ideally I could use something like FOR XML PATH('FieldValues')
You can build your XML as a string using for xml path('') and then cast to XML.
select T.Id,
cast('<FieldValues>' + (
select '<'+T2.Field+'>'+
(select T2.Value as '*' for xml path(''))+
'</'+T2.Field+'>'
from dbo.YourTable as T2
where T.Id = T2.Id
for xml path(''), type
).value('text()[1]', 'varchar(max)') +
'</FieldValues>' as xml) as FieldValues
from dbo.YourTable as T
group by T.Id;
SQL Fiddle
This part (select T2.Value as '*' for xml path('')) is there to take care of characters that needs to be entities in the value like &.
Here is one way:
SELECT '<FieldValues>'+
'<FirstName>'+fn.Value +'</FirstName>' +
'<LastName>'+ln.Value +'</LastName>' +
'<Age>'+age.Value +'</Age>' +
'<Gender>'+gender.Value +'</Gender>' +
'</FieldValues>
FROM (SELECT DISTINCT ID FROM userInfo) t
JOIN userInfo fn ON t.ID = fn.ID and fn.Field = 'FirstName'
JOIN userInfo ln ON t.ID = ln.ID and ln.Field = 'LastName'
JOIN userInfo age ON t.ID = age.ID and age.Field = 'Age'
JOIN userInfo gender ON t.ID = gender.ID and gender.Field = 'Gender'
How this works:
First I create table of just the unique ID numbers.
SELECT DISTINCT ID FROM table
Then I use this table to join back to the main table for each field. (Each of these joins will have only one row per ID.)
JOIN table fn ON t.ID = fn.ID and fn.Field = 'FirstName'
JOIN table ln ON t.ID = ln.ID and ln.Field = 'LastName'
JOIN table age ON t.ID = age.ID and age.Field = 'Age'
JOIN table gender ON t.ID = gender.ID and gender.Field = 'Gender'
Finally I create a string formatted as you need.
'<FieldValues>'+
'<FirstName>'+fn.Value +'</FirstName>' +
'<LastName>'+ln.Value +'</LastName>' +
'<Age>'+age.Value +'</Age>' +
'<Gender>'+gender.Value +'</Gender>' +
'</FieldValues>
An additional note: It is recommended to not use Camel Case on your xml since xml is case sensitive any use of case is a pain -- most just use all lower case.

Format output in SQL so that multiple values of column b are associated with one value of column a

This is my select statement:
SELECT lastName + ',' + firstName as Name,
classNumber
FROM STUDENT
RIGHT JOIN ENROLL ON Enroll.stuid = Student.stuid
and the output:
name classnumber
--------------------------
Smith,Tom ART103A
Smith,Tom HST205A
Chin,Ann ART103A
Chin,Ann CSC201A
Chin,Ann MTH103A
McCarthy,Owen ART103A
McCarthy,Owen MTH103C
Rivera,Jane CSC201A
Rivera,Jane MTH101B
I would like it so that each name only appears in the first column once. Any help or idea greatly appreciated.
As the rows are all independent from each other, no row can know if it's the first one.
What you could do is to GROUP BY Name and put all the classNumbers together in a list like this:
SELECT lastName + ',' + firstName AS Name,
GROUP_CONCAT(classNumber SEPARATOR '\n') AS classNumbers
FROM student
RIGHT JOIN Enroll ON (Student.stuid = Enroll.stuid)
GROUP BY Name
if only one class will appear.
SELECT lastName + ',' + firstName as Name, classNumber
FROM STUDENT
RIGHT JOIN
(select Student.stuid as stuid, min(classNumber) as ClassNumber
from ENROLL Enroll inner Join Student
ON (Student.stuid = Enroll.stuid) group by Student.stuid) AS NewEnrol
ON NewEnroll.stuid = Student.stuid
Or
SELECT lastName + ',' + firstName as Name,Min(classNumber)
FROM STUDENT
RIGHT JOIN ENROLL ON Enroll.stuid = Student.stuid
Group By lastname+','+firstname

How can I create multiple columns from one DB Field in SQL Server?

I have a field called PropertyValue in the UserProfile table that can contain information for address, phone number, first name, last name, user name, city, etc... each record in this table is associated to a user by UserId, it is also associated to a ProfilePropertyDefinition which contains the definition for each of the properties (ie. PropertyName).
Through this relationship I can get all of the property values along with their property names. What I would like to do it to extract the data from these two columns (PropertyValue, PropertyName) and create a table similar to this:
First Name | Last Name | Email | Phone | City | Country
-------------------------------------------------------
| | | | |
So, I wanted to know if I can use a SQL statement to do this, here's my go at it:
SELECT FirstName = (SELECT PropertyValue FROM UserProfile WHERE PropertyDefinitionID = (SELECT PropertyDefinitionID WHERE PropertyName = 'first name')),
LastName = (SELECT PropertyValue FROM UserProfile WHERE PropertyDefinitionID = (SELECT PropertyDefinitionID WHERE PropertyName = 'last name')),
Email = (SELECT PropertyValue FROM UserProfile WHERE PropertyDefinitionID = (SELECT PropertyDefinitionID WHERE PropertyName = 'email'))
But that didn't work and something seems really weird about it... Anyone know how to take one column and display it's values in several different columns?
SELECT fn.PropertyValue FirstName,
ln.PropertyValue LastName,
etc...
From UserProfile fN
Join UserProfile lN
On fN.PropertyName = 'first name'
And ln.PropertyName = 'last name'
And fn.user = ln.user
Join UserProfile eM
On fN.PropertyName = 'first name'
And eM.PropertyName = 'email'
And eM.user = fn.user
(
Personally, I would stop right now and consider how bad this design will be for performance. This is in general a very poor technique to use to store this type of data. If you have 20 proerties you want to display you will have to join (And left join at that as you can't guarantee each property will be represented) to this table 20 times. Further, if this is central to your data structure (As it sounds like it is from the type of data you seem to be storing) virtually every query will need to do something simliar and performance will be atrocious. There are time when this is the best model (when you have no way of knowing in advance what properties will need to be stored), but most of the time, it's use is a sign of a bad design.
http://en.wikipedia.org/wiki/Entity-Attribute-Value_model
I guess you could do a select from the same table multiple times.
Let say tA is the name table with UserProfileID,PropertyDefinition and PropertyValue
You could do
select
t1.PropertyValue as FirstName,
t2.PropertyValue as LastName,
...
FROM
tA as t1, tA as t2, ....
WHERE
t1.PropertyDefinition Like 'FirstName' AND
t2.PropertyDefinition Like 'LastName' AND
....
AND
t1.UserId = #user AND
t2.UserID = #user ....
Not ideal, but it would work
You need to join the tables many times over (as many times as you have fields):
SELECT UPFN.PropertyValue AS FirstName, UPLN.PropertyValue AS LastName, ...
FROM UserProfile UPFN
INNER JOIN ProfilePropertyDefinition PPDFN ON PPDFN.PropertyDefinitionID = UPFN.PropertyDefinitionID AND PPDFN.PropertyName = 'first name'
INNER JOIN UserProfile UPLN ON UPLN.id = UPFN.id
INNER JOIN ProfilePropertyDefinition PPDLN ON PPDLN.PropertyDefinitionID = UPLN.PropertyDefinitionID AND PPDLN.PropertyName = 'last name'
...
Note that this relies on their being some ID field in the UserProfile that you can use to tie all the rows for the same user together.
Assuming schema like
UserProfile#
{userid,
ProfileName,
propertyValue
}
You would want to do
SELECT
FirstName.PropertyValue FirstNAme,
LastName.PropertyValue LastName,
FROM
users
JOIN (USERPROFILE) FirstName ON
FirstName.userid = users.userid
and PropertName ='FirstName'
JOIN (USERPROFILE) LastName ON
LastName.userid = users.userid
and PropertName ='LastName'
I would write the query like this:
Select
aa.userId,
Coalesce(Max(Case when PropertyName = 'First Name' then PropertyValue else '' end),'') as FirstName,
and so on
from
UserTable as aa
left join
UserProfile as bb
on
aa.UserId = bb.UserId
left join
ProfilePropertyDefinition as cc
on bb.PropertyDefinitionId = cc.PropertdefinitionId
group by
aa.UserId
I would need to know more about your table sturtures and what you are trying to achomplish but an option may be to create a SQL Scalar Function to retieve the values of the properties. I am making some assumptions on table names and database setup but try this on...
CREATE FUNCTION [dbo].[UserProperty]
(
#UserProfileID UNIQUEIDENTIFIER, #Property VARCHAR(200)
)
RETURNS VARCHAR(max)
AS
BEGIN
-- Declare the return variable here
DECLARE #Value AS VARCHAR(MAX)
SELECT #Value = PropertyValue FROM UserProfile up INNER JOIN PropertyDefinitions pd ON
up.PropertyDefinitionID = pd.PropertyDefinitionID
WHERE pd.PropertyName = #Property AND up.UserProfileID=#UserProfileID
RETURN ISNULL(#Value,'')
END
SELECT
[dbo].[UserProperty](UserProfileID, 'first name') AS [First Name],
[dbo].[UserProperty](UserProfileID, 'last name') AS [Last Name],
[dbo].[UserProperty](UserProfileID, 'email') AS [Email]
FROM
[Users]

Resources