SQL SELECT from and EAV table - sql

Based on my previous questions I have this following tables:
IrisColor
ID Description
1 Blue
2 Gray
3 Green
4 Brown
SkinColor
ID Description
1 White
2 Asian
3 Dark
Gender
ID Description
1 Male
2 Female
And the Attributes table
Attributes
ID Description
1 SkinColor
2 IrisColor
3 Gender
And also the EAV table:
PersonDetails
PersonID AttributeID ValueID
121 1 1
121 2 2
121 3 1
122 1 2
122 2 1
122 3 1
So if I would want to select the Name, the attribute name and value for only the SkinColor I would do something like this:
SELECT p.Name,
a.Description,
v.Description
FROM PersonDetails AS sd
INNER JOIN Subjects AS p ON sd.PersonID=p.ID
INNER JOIN SubjectAttributes AS a ON sd.AttributeID=a.ID
INNER JOIN SkinColor AS v ON sd.ValueID= v.ID
But what should I do If I would want to select all the information for all of the persons from the database, not only skin color but iris color and gender too?
Previously I knew that from SkinColor I wanted to select that value, but in the PersonDetails I also have Values for IrisColor and Gender.
INNER JOIN SkinColor AS v ON sd.ValueID= v.ID this won't be appropriate anymore. How to replace this with something more dynamical?
Update:
I used this statement:
SELECT
SubjectID,
SkinColor,
IrisColor,
EyeLidFlisure,
KnownEyeDeffect,
Ethnicity,
Height,
DrivingLicense,
Gender
FROM
(
SELECT SubjectID, attr.Description as attribute, ValueID from SubjectDetails, SubjectAttributes as attr WHERE SubjectDetails.AttributeID=attr.ID
) as t
PIVOT(MAX(ValueID) FOR attribute IN (SkinColor,IrisColor,Gender,EyeLidFlisure,KnownEyeDeffect,Ethnicity,Height,DrivingLicense)) AS t1
Now, I have all the attributes listed in separate columns, but instead of Value description I have Value ID. How should I continue?

Guess you need dynamic sql built from attributes dictionary
declare #sql varchar(max)=
'select PersonID , ' +
+ stuff((select ','+Description + '.Description as ' + Description
from Attributes
for xml path ('')),1,1,'')
+ ' from (select PersonID, '
+ stuff((select ',max(case AttributeID when ' + cast(ID as varchar(5)) +' then ValueID end) as ' + Description
from Attributes
for xml path ('')),1,1,'')
+' from PersonDetails group by PersonID ) pvt'
+ (select ' left join ' + Description + ' on pvt.' + Description + ' = '+ Description + '.ID'
from Attributes
for xml path (''));
exec (#sql);

Your data model is a bit arcane, because you have ids in the "person details" table that cannot have proper foreign key relationships. You could put all the attributes in the same table. Or have a separate table for each attribute. Or -- as is common with EAV models -- put the descriptions directly into PersonDetails.
You are going to need to do something like this:
SELECT p.Name,
sa.Description,
ic.Description as IrisColor,
g.Description as gender
FROM PersonDetails sd INNER JOIN
Subjects p
ON sd.PersonID = p.ID INNER JOIN
SubjectAttributes sa
ON sd.AttributeID = sa.ID
ON LEFT JOIN
SkinColor sc
ON sd.ValueID = sc.ID AND sa.Description = 'SkinColor' LEFT JOIN
IrisColor ic
ON sd.ValueId = ic.ID AND sa.Description = 'IrisColor' LEFT JOIN
Gender g
ON sd.ValueId = g.ID AND sa.Description = 'Gender';

This would be the complete solution (don't know if it is the easiest one.. but it works)
WITH Subject AS (
SELECT
SubjectID,
SkinColor,
IrisColor,
EyeLidFlisure,
KnownEyeDeffect,
Ethnicity,
Height,
DrivingLicense,
Gender
FROM
(
SELECT SubjectID, attr.Description as attribute, ValueID from SubjectDetails, SubjectAttributes as attr WHERE SubjectDetails.AttributeID=attr.ID
) as t
PIVOT(MAX(ValueID) FOR attribute IN (SkinColor,IrisColor,Gender,EyeLidFlisure,KnownEyeDeffect,Ethnicity,Height,DrivingLicense)) AS t1
)
SELECT SubjectID,
whole.Name as Name,
whole.eMail as eMail,
skincolor.Description as SkinColor,
iriscolor.Description as IrisColor,
eyelid.Description as EyeLidFlisure,
defect.Description as KnownEyeDeffect,
eth.Description as Ethnicity,
height.Description as Height,
dl.Description as DrivingLicense,
gender.Description as Gender
FROM Subject S
Left JOIN Subjects whole ON whole.ID=S.SubjectID
Left JOIN SkinColor skincolor ON S.SkinColor=skincolor.ID
Left JOIN IrisColor iriscolor ON S.IrisColor=iriscolor.ID
Left JOIN EyeLidFlisure eyelid ON S.EyeLidFlisure=eyelid.ID
Left JOIN KnownEyeDeffect defect ON S.KnownEyeDeffect=defect.ID
Left JOIN Ethnicity eth ON S.Ethnicity=eth.ID
Left JOIN Height height ON S.Height=height.ID
Left JOIN DrivingLicense dl ON S.DrivingLicense=dl.ID
Left JOIN Gender gender ON S.Gender=gender.ID

Related

Use Data of 1 table into another one dynamically

I have one table category_code having data like
SELECT Item, Code, Prefix from category_codes
Item Code Prefix
Bangles BL BL
Chains CH CH
Ear rings ER ER
Sets Set ST
Rings RING RG
Yellow GOld YG YG........
I have another table item_categories having data like
select code,name from item_categories
code name
AQ.TM.PN AQ.TM.PN
BL.YG.CH.ME.PN BL.YG.CH.ME.PN
BS.CZ.ST.YG.PN BS.CZ.ST.YG.PN
CR.YG CR.YG.......
i want to update item_categories.name column corresponding to category_code.item column like
code name
BL.YG.CH.ME.PN Bangles.Yellow Gold.Chains.. . . .
Please suggest good solution for that. Thanks in advance.
First, split the code into several rows, join with the category code and then, concat the result to update the table.
Here an example, based on the data you gave
create table #category_code (item varchar(max), code varchar(max), prefix varchar(max));
create table #item_categories (code varchar(max), name varchar(max));
insert into #category_code (item, code, prefix) values ('Bangles','BL','BL'),('Chains','CH','CH'),('Ear rings','ER','ER'), ('Sets','Set','ST'),('Rings','RING','RG'), ('Yellow gold','YG','YG');
insert into #item_categories (code, name) values ('AQ.TM,PN','AQ.TM.PN'),('BL.YG.CH.ME.PN','BL.YG.CH.ME.PN'),('BS.CZ.ST.YG.PN','BS.CZ.ST.YG.PN')
;with splitted as ( -- split the codes into individual code
select row_number() over (partition by ic.code order by ic.code) as id, ic.code, x.value, cc.item
from #item_categories ic
outer apply string_split(ic.code, '.') x -- SQL Server 2016+, otherwise, use another method to split the data
left join #category_code cc on cc.code = x.value -- some values are missing in you example, but can use an inner join
)
, joined as ( -- then joined them to concat the name
select id, convert(varchar(max),code) as code, convert(varchar(max),coalesce(item + ',','')) as Item
from splitted
where id = 1
union all
select s.id, convert(varchar(max), s.code), convert(varchar(max), j.item + coalesce(s.item + ',',''))
from splitted s
inner join joined j on j.id = s.id - 1 and j.code = s.code
)
update #item_categories
set name = substring (j.item ,1,case when len(j.item) > 1 then len(j.item)-1 else 0 end)
output deleted.name, inserted.name
from #item_categories i
inner join joined j on j.code = i.code
inner join (select code, max(id)maxid from joined group by code) mj on mj.code = j.code and mj.maxid = j.id

How to combine multiple rows, merging cells with different values across columns and rows using SQL

Below is the form of data I have right now from a SQL query:
ID Name Nationality Institution Degree Result
---------------------------------------------
1 Brian USA a b c
1 Brian USA d e f
1 Brian USA h i j
2 Faye UK y z x
2 Faye UK o p q
And the data would ideally be sorted as below:
ID Name Nationality Background
-------------------------------------------------
1 Brian USA a,b,c; d,e,f; h,i,j
2 Faye UK y,z,x; o,p,q
I'm a SQL beginner and I'd very much appreciate any help with this.
Below is my current SQL query:
select
table1.id,
table1.lastname,
table1.firstname,
table1.group,
table2.institution,
table2.degree,
table2.result,
from
table1
inner join
table2 on (table1.id = table2.id)
where
((table1.startyear = '2017')
and (table1.group = 'A'))
You can query as below:
Select Id, [Name], Nationality,
Background = Stuff((Select '; '+Institution+','+Degree+','+Result from #table1 where id = t.Id for xml path('')),1,2,'')
from #table1 t
Group by Id, [Name], Nationality
So using list() and the || string concat in Firebird ( IBExpert ) gives us:
UNTESTED: List() Doc requires 2.1 version or greater.
A prior SO answer using List()
Note Group is on the reserved words list; so you may have to escape it using " in firebird
SELECT table1.id
, table1.lastname
, table1.firstname
, table1."group"
, List(table2.institution ||','||
table2.degree ||','||
table2.result,';') as Background
FROM table1
INNER JOIN table2
ON table1.id = table2.id
WHERE table1.startyear = '2017'
AND table1."group" = 'A'
GROUP BY table1.id
, table1.lastname
, table1.firstname
, table1."group"

How do you get the last record generated in a recursive

I am using recursive in sql 2014 in below code.
WITH products AS
(
SELECT
prd.productID,
prd.MainproductID,
prc.Price,Level = 0,
Row_ID = CAST(ROW_NUMBER() OVER (ORDER BY prd.productID) AS VARCHAR(MAX)),
Level2 = CAST(prd.productID AS VarChar(Max))
FROM
PrdTable prd
INNER JOIN prd_priceList (NOLOCK) prc ON prd.productID= prc.productID
WHERE prd.MainproductID IS NULL
UNION ALL
SELECT
prd.productID,
prd.MainproductID,
prc.Price,Level +1,
prcRec.Row_ID + '.' + CAST(ROW_NUMBER() OVER (PARTITION BY prd.MainproductID ORDER BY prd.productID) AS VARCHAR(MAX)),
Level2 = CAST(prcRec.productID AS VarChar(Max)) + ', ' + prcRec.Level2
FROM
products prcRec
INNER JOIN PrdTable prd ON prcRec.productID= prd.MainproductID
INNER JOIN prd_priceList (NOLOCK) prc ON prd.productID= prc.productID
)
select
productID ,
MainproductID ,
Level,
Row_ID,
Level2
from prodproductsucts
And this return like result in attachment screenshot. I want only select bottom line of each level.If doesn't have child then will select its. But if has child then will go to latest level and pick latest one. I alsa paint yellow rows which i need to select in screenshot.
If I understand your requirements...
In the final query just add the following WHERE
... Where productID not in (Select Distinct ISNULL(MainProductID,-999) From products)

SQL join to many rows

I have a sql Query with a lot of queries.
But i will try to make it simple
Table.Result
Id
Result
Table.ResultGender
ResultID
GenderID
Table.Gender
Id
Gender
One result can have more than one gender.
So want a result like this
Result | Gender
Some result | Female
Another result | Female, male
But i'm getting
Result | Gender
Some result | Female
Another result | male
Another result | Female
Query:
SELECT Gender.Name , Result.Result
FROM Gender
LEFT OUTER JOIN ResultGender ON Gender.Id = ResultGender.GenderId
LEFT OUTER JOIN Result ON ResultGender.ResultId = Result.Id
UPDATE
I have tried this
SELECT Gender.Name , Result.Result ,
STUFF((SELECT ',' + Name
FROM Gender
WHERE (Id = Gender_1.Id) FOR XML PATH(''))as varchar(max)) AS test
FROM Gender AS Gender_1
LEFT OUTER JOIN ResultGender ON Gender_1.Id = ResultGender.GenderId
LEFT OUTER JOIN Result ON ResultGender.ResultId = Result.Id
AND THIS in SQL manager
SELECT Gender.Name , Result.Result ,
CAST((SELECT ',' + Name
FROM Gender
WHERE (Id = Gender_1.Id) FOR XML PATH(''))as varchar(max)) AS test
FROM Gender AS Gender_1
LEFT OUTER JOIN ResultGender ON Gender_1.Id = ResultGender.GenderId
LEFT OUTER JOIN Result ON ResultGender.ResultId = Result.Id
BOTH are trying to save a RPT file
Try in following:
DECLARE #Result VARCHAR(8000)
SELECT Gender.Name, #Result = COALESCE(#Result + ', ', '') + Result.Result
FROM Gender
JOIN Result ON Gender.Id = Result.Id
JOIN ResultGender ON Gender.Id = ResultGender.GenderId
AND StudyResult.Id = ResultGender.ResultId
You are joining incorrectly. Gender.Id is the ID of a Gender record, and Result.Id is the ID of a Result record. Hence Gender.Id = Result.Id makes no sense. ResultGender is the brige table you need for the join: Gender -> GenderResult -> Result.
As to your actual request: Group by Result in order to get one record per Result. Get your Gender list with an appropriate function (I'm showing it here with MySQL's GROUP_CONCAT.)
select
r.result
group_concat(g.name)
from result r
inner join resultgender rg on rg.resultid = r.id
inner join gender g on g.id = rg.genderid
group by r.result;
Unfortunately SQL Server doesn't provide such function as GROUP_CONCAT. It needs some fumbling with XML PATH and STUFF instead. See here: How to make a query with group_concat in sql server.
In case you have Results without a Gender, use outer joins instead:
left join resultgender rg on rg.resultid = r.id
left join gender g on g.id = rg.genderid

How to combine values in SQL stored procedure

I have a stored procedure
SELECT P.Name,P.Description,
PP.Attribute,PD.Value
FROM admin.Profiles P
LEFT JOIN admin.ProfilePreferenceMap PPM on P.ProfileID=PPM.ProfileID
LEFT JOIN admin.ProfilePrefDtl PD on PD.ProfilePrefDtlID=PPM.PreferenceID
LEFT JOIN admin.ProfilePref PP on PP.ProfilePrefID=PD.ProfilePrefID
WHERE P.Name='Profile1'
which returns the rows like
Name Description Attribute Value
Profile1 Profile 1 description E EV
Profile1 Profile 1 description S SV
I would like to get the result as
Name Description Attribute Value
Profile1 Profile 1 description E,S EV,SV
My purpose is to convert this values to JSON data like -
[{"Name":"Profile1","Description":"Profile 1 description","Attribute":["E", "S"],"Value":["EV", "SV"]}]
You should emulate GROUP_CONCAT() which exists in MySql
SQLFiddle demo
with t as
(
SELECT P.Name,P.Description,
PP.Attribute,PD.Value
FROM admin.Profiles P
LEFT JOIN admin.ProfilePreferenceMap PPM on P.ProfileID=PPM.ProfileID
LEFT JOIN admin.ProfilePrefDtl PD on PD.ProfilePrefDtlID=PPM.PreferenceID
LEFT JOIN admin.ProfilePref PP on PP.ProfilePrefID=PD.ProfilePrefID
WHERE P.Name='Profile1'
)
select
Name,Description,
stuff((select ', ' + Attribute
from t t2 where t1.NAme = t2.NAme and t1.Description = t2.Description
for xml path('')),
1,2,'') [Attributes],
stuff((select ', ' + Value
from t t2 where t1.NAme = t2.NAme and t1.Description = t2.Description
for xml path('')),
1,2,'') [Values]
from t t1
group by Name,Description
You could use group by to group by unique name or description column but i'm not sure there is a easy way to get the comma separated value, made of values across separate rows, unless you did something with Temp tables and CONCAT() possibly?
If grouping is ok try this:
SELECT P.Name,P.Description,
PP.Attribute,PD.Value
FROM admin.Profiles P
LEFT JOIN admin.ProfilePreferenceMap PPM on P.ProfileID=PPM.ProfileID
LEFT JOIN admin.ProfilePrefDtl PD on PD.ProfilePrefDtlID=PPM.PreferenceID
LEFT JOIN admin.ProfilePref PP on PP.ProfilePrefID=PD.ProfilePrefID
WHERE P.Name='Profile1'
GROUP BY P.Name
However, grouping like this will only show the first row (based on sort) of your Attribute and Value columns howe