我有一张人员表。 每个人在PersonNames表中都可以有许多名称。 我想为每个人,他的2个名字和2个他的姓氏连续得到。 像这样:
PersonId | FName1 | Fname2 | Lname1 | Lname2
1 David Daniekl Bekernman Stivens
人表:
PersonId
BirthDate
PersonNames 表格:
PersonId
NameId
Name
NameType (e.g; first, last...)
感谢。
答案 0 :(得分:4)
如果这只是具体只有两个名字,这将选择每人MIN和MAX NameId的名字......
SELECT
Person.PersonId,
MAX(CASE WHEN Name.nameId = map.minNameId AND Name.nameType = 'first' THEN Name.Name ELSE NULL END) AS fname1,
MAX(CASE WHEN Name.nameId = map.minNameId AND Name.nameType = 'last' THEN Name.Name ELSE NULL END) AS lname1,
MAX(CASE WHEN Name.nameId = map.maxNameId AND Name.nameType = 'first' THEN Name.Name ELSE NULL END) AS fname2,
MAX(CASE WHEN Name.nameId = map.maxNameId AND Name.nameType = 'last' THEN Name.Name ELSE NULL END) AS lname2
FROM
Person
LEFT JOIN
(SELECT personId, MIN(nameId) AS minNameId, MAX(nameId) as maxNameId FROM PersonNames GROUP BY PersonId) AS map
ON map.PersonId = Person.PersonId
LEFT JOIN
PersonNames AS Name
On Name.PersonId = Person.PersonId
GROUP BY
Person.PersonId
的修改
现在我可以看到这是MS SQL Server,还有另一种选择。这里和其他人类似,但可能稍微简单......
WITH
sequenced_names AS
(
SELECT
DENSE_RANK() OVER (PARTITION BY PersonId ORDER BY NameID) AS NameOrdinal,
*
FROM
PersonNames
)
SELECT
PersonID,
MAX(CASE WHEN nameOrdinal = 1 AND nameType = 'first' THEN Name END) AS fname1,
MAX(CASE WHEN nameOrdinal = 1 AND nameType = 'last' THEN Name END) AS lname1,
MAX(CASE WHEN nameOrdinal = 2 AND nameType = 'first' THEN Name END) AS fname2,
MAX(CASE WHEN nameOrdinal = 2 AND nameType = 'last' THEN Name END) AS lname2
FROM
sequenced_names
GROUP BY
PersonID
答案 1 :(得分:2)
您可以通过XML列稍微绕道而行。
;with P(PersonID, Names) as
(
select PersonID,
(select NameType as '@NameType',
Name as '*'
from PersonNames as PN
where PN.PersonId = P.PersonId
for xml path('Name'), type)
from Person as P
)
select P.PersonID,
P.Names.value('(/Name[@NameType = "first"])[1]', 'varchar(100)') as FName1,
P.Names.value('(/Name[@NameType = "last"])[1]', 'varchar(100)') as LName1,
P.Names.value('(/Name[@NameType = "first"])[2]', 'varchar(100)') as FName2,
P.Names.value('(/Name[@NameType = "last"])[2]', 'varchar(100)') as LName2
from P
答案 2 :(得分:2)
1
,2
,-1
,-2
的名称。以下是查询的外观:
WITH ranked AS (
SELECT
*,
rnk =
CASE NameType WHEN 'first' THEN 1 ELSE -1 END *
ROW_NUMBER() OVER (
PARTITION BY PersonId, NameType
ORDER BY NameId
)
FROM PersonNames
WHERE NameType IN ('first', 'last')
)
SELECT
PersonId,
FName1 = [1],
FName2 = [2],
LName1 = [-1],
LName2 = [-2]
FROM (
SELECT
PersonId,
Name,
rnk
FROM ranked
) s
PIVOT (
MAX(Name) FOR rnk IN ([-2], [-1], [1], [2])
) p
另一方面,从最后对姓名进行排名似乎更合适(至少,我认为,我可能更喜欢这种方式)。所以这里是上面脚本的替代品,它从最后对姓氏进行排名:
WITH ranked AS (
SELECT
*,
rnk =
CASE NameType WHEN 'first' THEN 1 ELSE -1 END *
ROW_NUMBER() OVER (
PARTITION BY PersonId, NameType
ORDER BY CASE NameType WHEN 'first' THEN 1 ELSE -1 END * NameId
)
FROM PersonNames
WHERE NameType IN ('first', 'last')
),
SELECT
PersonId,
FName1 = [1],
FName2 = [2],
LName1 = ISNULL([-2], [-1]),
LName2 = CASE WHEN [-2] IS NULL THEN NULL ELSE [-1] END
FROM (
SELECT
PersonId,
Name,
rnk
FROM ranked
) s
PIVOT (
MAX(Name) FOR rnk IN ([-2], [-1], [1], [2])
) p
你可以看到这个版本有点复杂。首先,必须对NameId
应用符号更改,以确保其针对不同类型的不同方向进行排序。
另一件事是拉动最终结果集。你看,如果一个人只有两个或多个姓氏,那么脚本会按照表中的顺序显示它们:最后一个项目转到LName1
,最后一个项目转到{{1 }}。但是如果只有一个姓氏,我的意图是将其显示为LName2
,并将LName1
显示为空(就像第一个查询一样)。因此,正如您所看到的,必须采取其他措施来确保显示顺序。
答案 3 :(得分:1)
试试这个,SQL Server解决方案,
假设表OF名称中还有PersonId列,每个人的名称不超过2个,否则我们需要子查询:
CREATE TABLE [dbo].[Person]
(
[PersonId] [int] PRIMARY KEY
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[PersonNames]
(
[PersonId] [int] NOT NULL,
[nameId] [int] NOT NULL,
[NAME] [varchar] (100) NOT NULL,
[NameType] [varchar] (10) NOT NULL
) ON [PRIMARY]
GO
INSERT Person VALUES(1),(2)
INSERT dbo.PersonNames(PersonId, NameId, Name, NameType)
SELECT 1,1,'John', 'first' UNION ALL
SELECT 1,1,'Doe', 'last' UNION ALL
SELECT 1,2,'Ioann', 'first' UNION ALL
SELECT 1,2,'Doeman', 'last' UNION ALL
SELECT 1,3,'Yonh', 'first' UNION ALL
SELECT 1,3,'Doesson', 'last' UNION ALL
SELECT 2,1,'John2', 'first' UNION ALL
SELECT 2,1,'Doe2', 'last' UNION ALL
SELECT 2,2,'Ioann2', 'first' UNION ALL
SELECT 2,2,'Doeman2', 'last' UNION ALL
SELECT 2,3,'Yonh2', 'first' UNION ALL
SELECT 2,3,'Doesson2', 'last'
SELECT
Person.PersonId,
FName1.NAME FName1,
LName1.NAME LName1,
FName2.NAME FName2,
LName2.NAME LName2
FROM Person
JOIN (
SELECT
ROW_NUMBER() OVER (PARTITION BY PersonId ORDER BY NameId) Ordinal,
PersonId, Name, NameId
FROM PersonNames
WHERE NameType = 'first'
) FName1
ON FName1.PersonId = Person.PersonId AND FName1.Ordinal=1
JOIN PersonNames LName1
ON LName1.PersonId = FName1.PersonId AND LName1.NameType = 'last' AND FName1.NameId = LName1.NameId
LEFT JOIN
(
SELECT
ROW_NUMBER() OVER (PARTITION BY PersonId ORDER BY NameId) Ordinal,
PersonId, Name, NameId
FROM PersonNames
WHERE NameType = 'first'
) FName2
ON FName2.PersonId = Person.PersonId AND FName2.NameId <> FName1.NameId AND FName2.Ordinal = 2
LEFT JOIN PersonNames LName2
ON FName2.PersonId = LName2.PersonId AND LName2.NameType = 'last' AND LName2.NameId <> LName1.NameId AND FName2.NameId = LName2.NameId
DROP TABLE Person
DROP TABLE dbo.PersonNames
答案 4 :(得分:0)
您可以在mysql中使用group_concat将它们放在一行中,然后您的应用程序可以确定要显示的内容。
SELECT Person.PersonId
, GROUP_CONCAT( DISTINCT first_name.Name SEPARATOR ',' ) AS first_names
, GROUP_CONCAT( DISTINCT last_name.Name SEPARATOR ',' ) AS last_names
FROM Person
LEFT JOIN PersonName first_name ON Person.PersonId = first_name.PersonId AND first_name.Type = 'first'
LEFT JOIN PersonName last_name ON Person.PersonId = last_name.PersonId AND last_name.Type = 'last'
GROUP BY Person.PersonId
您还可以在SQL中使用SUBSTRING和LOCATE来获取第一个和第二个条目,但该方法相当脆弱/复杂。