Patron Information - plch/sierra-sql GitHub Wiki

Non-Expired Cardholders

SELECT
	pr.ptype_code as ptype_code,
	ppn.description as ptype_description, 
	count(*) as count_patrons	
FROM
	sierra_view.record_metadata as rm 
	JOIN sierra_view.patron_record pr on (
		pr.record_id = rm.id
		and pr.ptype_code <= 51
	)
	JOIN sierra_view.ptype_property as pp on pp.value = pr.ptype_code 
	JOIN sierra_view.ptype_property_name as ppn on ppn.ptype_id = pp.id  
WHERE 
	rm.record_type_code = 'p'
	and rm.campus_code = ''
	and date(pr.expiration_date_gmt) > '2024-01-01' -- non-expired
GROUP BY 
	1,2
ORDER BY
	3 desc

Find counts of distinct patrons with checkouts by branch

select 
	bn."name" as branch_name,
	count(distinct ct.patron_record_id) as count_distinct_patrons
from 
	sierra_view.circ_trans as ct 
	join sierra_view.statistic_group as sg on sg.code_num = ct.stat_group_code_num 
	join sierra_view."location" as l on l.code = sg.location_code 
	join sierra_view.branch as b on b.code_num = l.branch_code_num 
	join sierra_view.branch_name bn on bn.branch_id = b.id 
where 
	ct.op_code = 'o' -- checkouts
	and ct.ptype_code::INTEGER < 196
group by 
	branch_name
order by 
	count_distinct_patrons desc

find counts of patrons created within the last year (2023-01-20)

SELECT 
	to_char(rm.creation_date_gmt, 'YYYY') as patron_created_year, 
	pr.ptype_code as ptype_code,
	ppn.description as ptype_description, 
	count(*) as count_patrons	
FROM
	sierra_view.record_metadata as rm 
	JOIN sierra_view.patron_record pr on (
		pr.record_id = rm.id
		and pr.ptype_code <= 51
	)
	JOIN sierra_view.ptype_property as pp on pp.value = pr.ptype_code 
	JOIN sierra_view.ptype_property_name as ppn on ppn.ptype_id = pp.id  
WHERE 
	rm.record_type_code = 'p'
	and rm.campus_code = ''
	and rm.creation_date_gmt >= '2022-01-01'
	and rm.creation_date_gmt < '2023-01-01'
	-- NOTE:
	-- We may want to include expired cards? 
	-- This would have the effect of increasing the count of "Online Registraion" cards
	--and pr.expiration_date_gmt::date >= now()::date 
GROUP BY 
	1,2,3
ORDER BY
	4 desc

Output Example:

patron_created_year ptype_code ptype_description count_patrons
2022 0 Unlimited 21274
2022 51 Online Registration 11229
2022 1 Child Only (7 Items) 10106
2022 2 Teen Only (7 Items) 3252
2022 3 Adult Only (7 Items) 1688
2022 10 Educator 883
2022 7 Downloadables Only 859
2022 5 Limited Juvenile Material Only 491
2022 12 Outreach-NO Rating Sheet 206
2022 6 Limited No Videos 86
2022 15 Bookclub 64
2022 32 Non-Resident Unlimited 49
2022 41 Visitor Limited to Print Mat 16
2022 30 Non-Resident Juv Mat Only 2
2022 40 Visitor Juv Print Mat Only 1
2022 50 Complimentary 1

find patron record info for patrons who have names that have been entered in all caps

SELECT 
	pr.home_library_code ,
	pr.ptype_code ,
	rm.record_num ,
	(
		SELECT 
			json_agg(
				v.field_content ORDER BY v.occ_num 
			)
		FROM
			sierra_view.varfield AS v
		WHERE 
			v.record_id = prf.patron_record_id 
			AND v.varfield_type_code = 'b'	
	) AS patron_record_barcodes,
--	rm.id AS patron_record_id,
	date(rm.creation_date_gmt) AS patron_record_creation_date ,
	date(rm.record_last_updated_gmt) AS patron_record_last_update ,
	date(pr.expiration_date_gmt) AS patron_record_expiration_date ,
	prf.last_name ,
	prf.first_name ,
	prf.middle_name 
FROM 
	sierra_view.patron_record_fullname AS prf
	JOIN sierra_view.record_metadata AS rm ON (rm.id = prf.patron_record_id AND rm.campus_code = '') 
	JOIN sierra_view.patron_record AS pr ON pr.record_id = prf.patron_record_id
WHERE 
	(
		last_name ~ '^[A-Z0-9]{1,}(\s|$)'
		OR prf.first_name ~ '^[A-Z0-9]{2,}(\s|$)' -- CHANGE the {1,} to {2,} TO work with or without initials respectively 
	)
	AND pr.ptype_code :: INTEGER < 196
ORDER BY 
	1 ASC,
	2 ASC,
	5 DESC

report on what fines would be purged based on a 5 year span

SELECT
'p' || r.record_num || 'a' as patron_record_num,
-- removing barcode unless we really need it
-- (
-- 	SELECT
-- 	e.index_entry as barcode
-- 	from sierra_view.phrase_entry as e
-- 
-- 	WHERE
-- 	e.record_id = r.id
-- 	AND e.index_tag || e.varfield_type_code = 'bb'
-- 
-- 	ORDER BY
-- 	e.occurrence asc
-- 
-- 	LIMIT 1
-- ) as barcode,
p.owed_amt as current_owed_amt,
-- may or may not want to flag occurrences where the computed amount is different than the owed_amt on the patron record
-- (
-- 	SELECT
-- 	CASE 
-- 		WHEN SUM( (f.item_charge_amt + f.processing_fee_amt + billing_fee_amt) - f.paid_amt ) = p.owed_amt
-- 		THEN true
-- 
-- 		ELSE false
-- 	END
-- 
-- 	FROM
-- 	sierra_view.fine as f
-- 
-- 	WHERE
-- 	f.patron_record_id = p.record_id
-- 
-- 	GROUP BY
-- 	f.patron_record_id
-- ) AS computed_equal_current,
(
	SELECT
	SUM( (f.item_charge_amt + f.processing_fee_amt + billing_fee_amt) - f.paid_amt )
	FROM
	sierra_view.fine as f
	WHERE
	-- the date of the statute of limitations fines we're looking for
	f.assessed_gmt <= date_trunc('day', (NOW() - INTERVAL '5 years') )
	AND f.patron_record_id = p.record_id
	GROUP BY
	f.patron_record_id
) AS sum_prev_statue_lim_date,
-- compute the amount after the purge
(	
	p.owed_amt - (
		SELECT
		SUM( (f.item_charge_amt + f.processing_fee_amt + billing_fee_amt) - f.paid_amt )
		FROM
		sierra_view.fine as f
		WHERE
		-- the date of the statute of limitations fines we're looking for (5 years prior to today's date)
		f.assessed_gmt <= date_trunc('day', (NOW() - INTERVAL '5 years') )
		AND f.patron_record_id = p.record_id
		GROUP BY
		f.patron_record_id
	)
) as post_purge_owed_amt,
date_trunc('day',p.expiration_date_gmt)::date as expiration_date,
date_trunc('day',p.activity_gmt)::date as activity,
(
	SELECT
	date_trunc('day', f.assessed_gmt)::date
	FROM
	sierra_view.fine as f
	WHERE
	f.patron_record_id = p.record_id
	ORDER BY
	f.assessed_gmt ASC
	LIMIT
	1
) as first_assessed_fine_date
FROM
sierra_view.record_metadata as r
JOIN
sierra_view.patron_record as p
ON
  r.id = p.record_id
WHERE
r.deletion_date_gmt is null
AND r.campus_code = ''
-- AND p.expiration_date_gmt >= '2017-06-09'
AND r.id IN (
	SELECT
	f.patron_record_id
	FROM
	sierra_view.fine as f
	WHERE
	-- the date of the statute of limitations fines we're looking for
	f.assessed_gmt <= date_trunc('day', (NOW() - INTERVAL '5 years') )
	GROUP BY
	f.patron_record_id
)
ORDER BY
p.owed_amt,
first_assessed_fine_date

count number of patron selfcheck sessions (grouped by patron and timestamp truncated to hour and then counted by statistic_group)

WITH selfcheck_statgroups AS (
SELECT
*
FROM
sierra_view.statistic_group_myuser

where
name ~* '^.*schk[0-9]{1,}'

ORDER BY
location_code,
name
)

SELECT
s.*,
(
	WITH patron_hour_counts AS (
	SELECT
	date_trunc('hour', c.transaction_gmt) as hour,
	c.patron_record_id

	-- , COUNT(*)

	FROM
	sierra_view.circ_trans as c
	where
	c.stat_group_code_num = s.code

	GROUP BY
	hour,
	c.patron_record_id

	-- ORDER BY
	-- c.id
	)

	SELECT COUNT(*) FROM patron_hour_counts
)
FROM
selfcheck_statgroups as s

-- JOIN
-- sierra_view.circ_trans as c
-- ON
--   c.stat_group_code_num = s.code
-- 
-- 
-- LIMIT 100
;

find patron emails from a given list of barcodes:

DROP TABLE IF EXISTS temp_barcodes;
CREATE TEMP TABLE temp_barcodes (
	id SERIAL NOT NULL,
	barcode VARCHAR(300)
);

INSERT INTO temp_barcodes (barcode) VALUES
-- one way to generate these lines from a spreadsheet:
-- use this function (assuming the barcode is in column A:
-- =CONCATENATE("(","""","b",A1,"""","),")
-- and then fill that function down in the spreadsheet, 
-- and then simply cut and paste below:
('bSR123'),
('bSR124')
;
---

SELECT
t.*,
(
	SELECT
	v.field_content
	FROM
	sierra_view.varfield as v

	WHERE
	v.varfield_type_code = 'z'
	AND v.record_id = e.record_id

	ORDER BY
	v.occ_num

	LIMIT 1
) as first_listed_email

FROM
temp_barcodes as t

LEFT OUTER JOIN
sierra_view.phrase_entry as e
ON
  (e.index_tag || e.index_entry) = LOWER(t.barcode)
;

find under 18 patrons (at the time their record was created) having unlimited card types. Group them by home library location and count them.

SELECT
p.home_library_code,
COUNT(*)

FROM
sierra_view.patron_record as p

JOIN
sierra_view.record_metadata as r
ON
  r.id = p.record_id

WHERE
r.campus_code = ''
-- unlimited card type
AND p.ptype_code = 0
-- between February 2016 - April 2019
AND r.creation_date_gmt::date >= '2016-02-01'::date
AND r.creation_date_gmt::date < '2019-05-01'::date
-- at patron record creation date AGE is under 18 years
AND ( AGE(p.birth_date_gmt) - AGE(r.creation_date_gmt) ) < INTERVAL '18 years'

GROUP BY
p.home_library_code

Export for "Contactable by Foundation"

SELECT
-- COUNT(*)
r.record_type_code || r.record_num || 'a' AS "RECORD #(PATRON)",
-- p.home_library_code as "HOME LIBRARY",
(
	SELECT
	l.name

	FROM
	sierra_view.location_myuser as l

	WHERE
	l.code = p.home_library_code
) AS "HOME LIBRARY",
p.birth_date_gmt::date AS "BIRTH DATE",
-- EXTRACT(YEAR FROM AGE(p.birth_date_gmt)) as "AGE",
p.checkout_total AS "TOT CHKOUT",
expiration_date_gmt::date AS "EXP DATE",
-- f.prefix AS "PATRN PREFIX NAME",
f.first_name AS "PATRN FIRST NAME",
f.middle_name AS "PATRN MIDDLE NAME",
f.last_name AS "PATRN LAST NAME",
-- f.suffix AS "PATRN SUFFIX NAME",
-- get the first listed address
(
	SELECT
	addr1

	FROM
	sierra_view.patron_record_address

	WHERE
	patron_record_id = p.record_id
	
	ORDER BY
	display_order

	LIMIT 1
) AS "ADDR1",

(
	SELECT
	city || ', ' || region || ' ' || postal_code

	FROM
	sierra_view.patron_record_address

	WHERE
	patron_record_id = p.record_id
	
	ORDER BY
	display_order

	LIMIT 1
) AS "CITY STATE ZIP",
(
	SELECT
	phone_number
	FROM
	sierra_view.patron_record_phone
	WHERE
	patron_record_id = p.record_id
	ORDER BY
	display_order
	LIMIT 1	
) AS "TELEPHONE",
(	
	SELECT
	field_content
	FROM
	sierra_view.varfield
	WHERE
	record_id = p.record_id
	AND varfield_type_code = 'z'
	ORDER BY
	occ_num
	LIMIT 1
) AS "EMAIL ADDR"

FROM
sierra_view.patron_record as p

JOIN
sierra_view.patron_record_fullname as f
ON
  f.patron_record_id = p.record_id

JOIN
sierra_view.record_metadata as r
ON
  r.id = p.record_id

WHERE
-- pcode3 is "Foundation? (P3)"
p.pcode3 = 2
AND expiration_date_gmt::date >= NOW()::date
-- patron is 18 or older
AND EXTRACT(YEAR FROM AGE(p.birth_date_gmt))::INTEGER > 17::INTEGER 
;

Find patrons with more than 1 name listed

SELECT
r.record_type_code || r.record_num || 'a' as record_number,
(
	SELECT
	string_agg(v.field_content, ',' order by v.occ_num)

	FROM
	sierra_view.varfield as v

	WHERE
	v.record_id = r.id
	AND v.varfield_type_code = 'b'
) as barcodes,

(
	SELECT
	string_agg(n.last_name || ' ' || n.first_name || ' ' || n.middle_name, ', ' order by n.display_order)

	FROM
	sierra_view.patron_record_fullname as n

	WHERE
	n.patron_record_id = r.id
) as fullnames_listed

FROM
sierra_view.record_metadata as r

JOIN
sierra_view.patron_record_fullname as f
ON
  f.patron_record_id = r.id

WHERE
r.record_type_code || r.campus_code = 'p'
and r.deletion_date_gmt is null

GROUP BY
r.id, r.record_type_code, r.record_num

HAVING
count(*) > 1

Find duplicate patrons


Find patron records that have duplicate barcodes

If barcode number is repeated on multiple records, this will display the patron record information

sample output:

created_date;barcode;patron_record_num;patron;ptype;last_circ_activity;expiration_date
2012-07-19 02:17:11-04;49143662;p1234567a;Patron, John Q 1947-02-26;0;2015-12-22 08:38:26-05;2020-12-22 04:00:00-05
2016-06-13 14:44:03-04;49143662;p1234568a;Patron, John Q 1947-02-26;0;2016-06-13 14:44:03-04;2021-06-13 04:00:00-04
2012-07-18 22:59:21-04;50948033;p1234569a;Patron, Patty L 1986-12-12;6;2015-05-26 12:30:29-04;2013-03-06 04:00:00-05
2013-06-15 16:07:41-04;50948033;p1234570a;Patty, Patty L 1986-12-12;0;2013-07-20 11:16:24.26-04;2018-06-15 04:00:00-04
SELECT
r.creation_date_gmt as created_date,
e.index_entry as barcode,
'p' || r.record_num || 'a' as patron_record_num,
n.last_name || ', ' ||n.first_name || COALESCE(' ' || NULLIF(n.middle_name, ''), '') || ' ' || p.birth_date_gmt as patron,
p.ptype_code as ptype,
p.activity_gmt as last_circ_activity,
p.expiration_date_gmt as expiration_date

FROM
sierra_view.phrase_entry as e

JOIN
sierra_view.patron_record as p
ON
  p.record_id = e.record_id

JOIN
sierra_view.record_metadata as r
ON
  r.id = p.record_id

JOIN
sierra_view.patron_record_fullname AS n
ON
  n.patron_record_id = r.id

WHERE 
-- combining the index tag with the index entry returns more quickly
e.index_tag || e.index_entry IN (
-- 'b' || '63160170',
-- 'b' || '63274153'
	SELECT
	'b' || e.index_entry as barcode

	FROM
	sierra_view.phrase_entry AS e

	JOIN
	sierra_view.patron_record as p
	ON
	  p.record_id = e.record_id

	WHERE
	e.index_tag || e.varfield_type_code = 'bb'

	GROUP BY
	barcode

	HAVING 
	count(*) > 1
)

ORDER BY
barcode,
patron ASC

Find patrons with multiple barcodes

SELECT
'p' || r.record_num || 'a' as record_num

FROM
sierra_view.record_metadata as r

WHERE
r.id IN
(
	SELECT
	-- count(*),
	e.record_id

	FROM
	sierra_view.patron_record as p

	JOIN
	sierra_view.phrase_entry as e
	ON
	  (e.record_id = p.record_id) AND (e.index_tag = 'b') AND (e.varfield_type_code = 'b')

	GROUP BY
	e.record_id

	HAVING
	count(*) > 1

	-- ORDER BY
	-- count(*)
)

Find duplicate patrons by name + birthdate + patron code

sample output

2012-07-18 21:30:10-04;12345678;p1234567a;Patron, John Q 1977-01-01;0;2016-08-18 16:11:55-04;2019-05-10 04:00:00-04;c;$145.78
2015-01-07 13:41:06-05;12345679;p7654321a;Patron, John Q 1977-01-01;0;2016-08-21 15:04:40-04;2020-04-10 04:00:00-04;c;$214.53
SELECT
r.creation_date_gmt as created,
e.index_entry as barcode,
'p' || r.record_num || 'a' as patron_record_num,
pn.last_name || ', ' ||pn.first_name || COALESCE(' ' || NULLIF(pn.middle_name, ''), '') || ' ' || pr.birth_date_gmt as patron,
pr.ptype_code,
pr.activity_gmt,
pr.expiration_date_gmt,
pr.mblock_code as block_code,
pr.owed_amt::float8::numeric::money as owed_amt
-- pr.home_library_code,

FROM
sierra_view.patron_record_fullname as pn

JOIN
sierra_view.patron_record as pr
ON
  pr.record_id = pn.patron_record_id

JOIN
sierra_view.record_metadata as r
ON
  r.id = pr.record_id

JOIN
sierra_view.phrase_entry AS e
ON
  (e.record_id = r.id) AND (e.index_tag = 'b') AND (e.varfield_type_code = 'b')
  
WHERE
pr.birth_date_gmt || pn.first_name || COALESCE(' ' || NULLIF(pn.middle_name, ''), '') || ' ' || pn.last_name 
IN
(
	SELECT

	p.birth_date_gmt ||
	n.first_name || COALESCE(' ' || NULLIF(n.middle_name, ''), '') || ' ' || n.last_name as patron_name
	-- e.index_entry,
	-- count(*) as matches

	FROM
	sierra_view.record_metadata AS r

	JOIN
	sierra_view.patron_record AS p
	ON
	  p.record_id = r.id

	JOIN
	sierra_view.patron_record_fullname AS n
	ON
	  n.patron_record_id = r.id

	-- JOIN
	-- sierra_view.phrase_entry AS e
	-- ON
	--   (e.record_id = r.id) AND (e.index_tag = 'b') AND (e.varfield_type_code = 'b')

	WHERE 
	r.record_type_code = 'p'
	-- and r.creation_date_gmt >= '2017-05-01'

	GROUP BY
	p.birth_date_gmt,
	patron_name,
	p.ptype_code
	-- e.index_entry

	HAVING
	COUNT(*) > 1
)

ORDER BY
pn.last_name || pn.first_name || pr.birth_date_gmt || COALESCE(' ' || NULLIF(pn.middle_name, ''), ''),
pr.ptype_code ASC,
pr.activity_gmt DESC

Find duplicate patrons by birthdate + name + barcode

sample output:

"1990-01-01";"John Q Patron";"12345678";2
"1977-02-02";"Betty J Patron"; "12345679";2
SELECT

p.birth_date_gmt,
n.first_name || COALESCE(' ' || NULLIF(n.middle_name, ''), '') || ' ' || n.last_name as patron_name,
e.index_entry,
count(*) as matches

FROM
sierra_view.record_metadata AS r

JOIN
sierra_view.patron_record AS p
ON
  p.record_id = r.id

JOIN
sierra_view.patron_record_fullname AS n
ON
  n.patron_record_id = r.id

JOIN
sierra_view.phrase_entry AS e
ON
  (e.record_id = r.id) AND (e.index_tag = 'b') AND (e.varfield_type_code = 'b')

WHERE 
r.record_type_code = 'p'
-- and r.creation_date_gmt >= '2017-05-01'

GROUP BY
p.birth_date_gmt,
patron_name,
e.index_entry

HAVING
COUNT(*) > 1

Find duplicate patron barcodes that start with “SR”

temporary patron cards are issued barcodes starting with SR, but are sometimes accidentally duplicated. This query will find those patron record nums.

sample output:

"p1234567a";"sr123456";"2017-04-23 11:20:59-04"
"p1234568a";"sr123456";"2017-04-04 15:11:56-04"
SELECT
-- patron record number, barcode, and create date. 
'p' || r.record_num || 'a',
p.index_entry,
r.creation_date_gmt

FROM
sierra_view.phrase_entry as p

JOIN
sierra_view.record_metadata as r
ON
  r.id = p.record_id

JOIN
sierra_view.patron_record as pr
ON
  pr.record_id = r.id

where 
p.index_tag || p.index_entry IN
(
SELECT
'b' || e.index_entry

FROM
sierra_view.phrase_entry as e

JOIN
sierra_view.record_metadata as r
ON
  r.id = e.record_id

WHERE
e.index_tag || e.index_entry SIMILAR TO 'b' || 'sr[0-9]{1,}'

GROUP BY
e.index_entry

HAVING COUNT(e.index_entry) > 1
)

ORDER BY 
r.creation_date_gmt DESC

Find patrons with note of "ConnectED"

SELECT
*

FROM

sierra_view.record_metadata as r

JOIN
sierra_view.patron_record  as p
ON
  p.record_id = r.id

JOIN
sierra_view.varfield as v 
ON
  (v.record_id = r.id) AND (v.varfield_type_code = 'x') AND (v.field_content = 'ConnectED')

where r.record_type_code = 'p'