I am very new to SQL and all self taught so please keep that in mind when getting too technical!
I am working with a Church database. We have a table that assigns a family ID to sets of people who are in a family. I have a table that contains all the family IDs and has a column with the unique person IDs that are associated with that family ID. When I run a query against the table looking for a specific family ID, I get a row for each unique person ID that is associated with that family ID.
What I need to do is combine the families into one row displaying the adult's first names in one column and the children's first names in another. Then the last name in a column and the address in a column.
I can do this successfully when I use this:
DECLARE @FirstNames VARCHAR (MAX)
DECLARE @Children VARCHAR (MAX)
DECLARE @Address VARCHAR (MAX)
DECLARE @LastName VARCHAR (MAX)
SELECT @FirstNames = COALESCE(@FirstNames+', ','') + pb.first_name,
@Address = pb.Address,
@LastName = pb.last_name
FROM core_v_person_basic pb
JOIN core_family_member fm ON fm.person_id = pb.person_id
WHERE fm.role_luid = 29
AND fm.family_id = 13783
SELECT @Children = COALESCE(@Children+', ','') + pb.first_name
FROM core_v_person_basic pb
JOIN core_family_member fm ON fm.person_id = pb.person_id
WHERE fm.role_luid = 31
AND fm.family_id = 13783
SELECT
@FirstNames AS 'First Names',
@LastName AS 'Last Name',
@Children AS 'Children',
@Address AS 'Address'
But the issue is this only works for one family ID. I want to be able to either display all the family IDs formatted this way, or display a certain set of family IDs. For example, I may want to display all the families that are in a certain class which will come from a different table.
I need a solution that is flexible enough for me to change it around to add different columns to the results based on need.
Thanks so much for any help!
UPDATE Here's a query that I modified from Dimitar Kyuchukov's answer: -- Simulate your tables for sample's sake (you would replace these variables with your table names in the code till the end of the sample):
DECLARE @core_family_member AS TABLE
(family_id int,
person_id int,
date_created datetime,
date_modified datetime,
created_by varchar(50),
modified_by varchar(50),
role_luid int,
organization_id int)
DECLARE @core_v_person_basic AS TABLE (
person_id int,
guid uniqueidentifier,
suffix varchar(50),
nick_name nvarchar(50),
first_name nvarchar(50),
last_name nvarchar(50),
birth_date datetime,
address_id int,
Address varchar(201),
street_address_1 varchar(100),
street_address_2 varchar(100),
city varchar(64),
state varchar(12),
postal_code varchar(24),
member_status_luid int,
member_status varchar(50),
record_status varchar(8),
gender varchar(1),
marital_status_luid int,
marital_status varchar(50),
home_phone varchar(50),
list_home_phone varchar(50),
business_phone varchar(50),
list_business_phone varchar(50),
cell_phone varchar(50),
list_cell_phone varchar(50),
email varchar(80),
area_id int,
area_name varchar(100),
organization_id int,
photo_guid varchar(80),
envelope_number int,
restricted bit)
INSERT INTO
@core_v_person_basic
SELECT * FROM core_v_person_basic
INSERT INTO
@core_family_member
SELECT * FROM core_family_member
-- Here children and parents are selected separately with a family ID to join by (these are called CTE-s for Common Table Expression - they will make the final statement more clear):
;WITH
Children AS (
SELECT
fm.family_id,
pb.first_name AS child_first_name
FROM
@core_v_person_basic pb
INNER JOIN @core_family_member fm
ON fm.person_id = pb.person_id
WHERE
fm.role_luid = 31
),
Parents AS (
SELECT
fm.family_id,
pb.first_name AS parent_first_name
FROM
@core_v_person_basic pb
INNER JOIN @core_family_member fm
ON fm.person_id = pb.person_id
WHERE
fm.role_luid = 29
),
Address AS (
SELECT
pb.address AS 'Address'
FROM
@core_v_person_basic pb)
-- Here stuff() function is used to concatenate the members grouped by family ID - basically an XML is "stuffed" similar to your variables and then the ready value is taken using the value() function:
SELECT
stuff((
SELECT
', ' + p.parent_first_name
FROM
Parents p
WHERE
f.family_id = p.family_id
FOR XML PATH(''), TYPE
).value('(./text())[1]', 'VARCHAR(MAX)')
, 1, 1, '') AS parents_names,
stuff((
SELECT
', ' + c.child_first_name
FROM
Children c
WHERE
f.family_id = c.family_id
FOR XML PATH(''), TYPE
).value('(./text())[1]', 'VARCHAR(MAX)')
, 1, 1, '') AS children_names
FROM
Parents f
GROUP BY
f.family_id
This is much closer to what I need, but I still can't add any more columns than what is already there. It seems to be because of the Group By syntax. Any help with how to bring up additional columns would be awesome!
SOLUTION! I got it working just as I need! Here's the working query with even more columns and information!
WITH
Children AS (
SELECT
fm.family_id,
pb.first_name AS child_first_name
FROM
core_v_person_basic pb
INNER JOIN core_family_member fm
ON fm.person_id = pb.person_id
WHERE
fm.role_luid = 31
),
Parents AS (
SELECT
fm.family_id,
pb.Address,
pb.city,
pb.state,
pb.postal_code,
pb.record_status,
pb.last_name,
pb.first_name AS parent_first_name
FROM
core_v_person_basic pb
INNER JOIN core_family_member fm
ON fm.person_id = pb.person_id
WHERE
fm.role_luid = 29
),
RecordStatus AS (
SELECT
fm.family_id,
pb.record_status AS record_status
FROM
core_v_person_basic pb
JOIN core_family_member fm On fm.person_id = pb.person_id
),
InactiveReason AS (
SELECT
fm.family_id,
ir.inactive_reason_value AS inactive_reason
FROM
core_person cp
JOIN core_family_member fm ON cp.person_id = fm.person_id
JOIN core_v_inactive_reason ir ON ir.inactive_reason_id = cp.inactive_reason_luid
)
-- Here stuff() function is used to concatenate the members grouped by family ID - basically an XML is "stuffed" similar to your variables and then the ready value is taken using the value() function:
SELECT
stuff((
SELECT
', ' + p.parent_first_name
FROM
Parents p
WHERE
f.family_id = p.family_id
FOR XML PATH(''), TYPE
).value('(./text())[1]', 'VARCHAR(MAX)')
, 1, 1, '') AS 'First Name(s)',
f.last_name,
stuff((
SELECT
', ' + c.child_first_name
FROM
Children c
WHERE
f.family_id = c.family_id
FOR XML PATH(''), TYPE
).value('(./text())[1]', 'VARCHAR(MAX)')
, 1, 1, '') AS 'Children',
f.Address,
f.city,
f.state,
f.postal_code,
STUFF((
SELECT
', ' + r.record_status
FROM
RecordStatus r
WHERE
f.family_id = r.family_id
FOR XML PATH(''), TYPE
).value('(./text())[1]', 'VARCHAR(MAX)')
, 1, 1, '') AS 'Record Status',
STUFF((
SELECT
', ' + i.inactive_reason
FROM
InactiveReason i
WHERE
f.family_id = i.family_id
FOR XML PATH(''), TYPE
).value('(./text())[1]', 'VARCHAR(MAX)')
, 1, 1, '') AS 'Record Status'
FROM
Parents f
GROUP BY
f.family_id,
f.Address,
f.city,
f.state,
f.postal_code,
f.last_name
Thanks everyone for the help!