A salt protects against an attacker that uses as rainbow table to pre-compute hashes.
But, if the space of the hashed values is small enough, the hashes can be reversed by brute-force regardless of whether they are salted or not.
For example, take credit card numbers, which are considered to be PII. Credit card numbers are 16 digits in length, so there are (only) 10^16 possible credit card numbers. (It's actually much smaller than this, because of the Luhn checksum in credit card numbers, but let's use 10^16 anyway).
Suppose the system is breached, and the value stored in the salted_hahsed_credit_card_number field of one of the records in the database is:
An attacker looks at the code, and sees that the ':' is the delimiter, the salt is the first value, and the salted hashed credit card number is the second value, and that credit card numbers are salted and hashed using one round sha256 hashing.
Using ASICS technology, an attacker can build a rig that does 100 terra-hashes per second for a cost of a few thousand dollars. So, in 100000 seconds (approx 28 hours), the attacker's rig can iterate through all 10^16 credit card numbers in the space, trying each one using a process like the one in the python script below, until it finds the one that produces the value above stored in the database. Sooner or later, it will find the correct card number, which is:
5105105105105100. This can be confirmed using the script below:
saltedhashedcreditcardnumber=hashlib.pbkdf2_hmac('sha256', creditcardnumberbytes, saltbytes, 1)
storedvalue=salthex + ':' + saltedhashedcreditcardnumberhex
print('stored value', storedvalue)
print('credit card number', creditcardnumber)
stored value 8947b8ef2ae54741eed7b359442cafdc:172e90126cccfe4a8117cce66a50aef96bb8f2263b838b175f072045132ab1d2
credit card number 5105105105105100
Using a more resource-intensive hash function (such as bcrypt, scrypt, or argon2) and/or using multiple rounds of hashing are effective ways of mitigating brute force attacks against salted hashing.