Trying to search on the last 4 of SSN (SQL) - sql

So I have found multiple resources that let me know how to retrieve the last 4 of a SSN. But I am trying to search the database for matches of the last 4 that I am provided. I am working with Oracle btw. I've tried a few different things but below is basically what I mean:
SELECT gm.plan_name, tpam.third_prty_admin_name, pm.partcpnt_first_name,
pm.partcpnt_last_name, pm.partcpnt_id, gm.grp_id,
tpam.third_prty_admin_id,
pm.partcpnt_birth_dt,pm.partcpnt_setup_dt
FROM gva_s01.partcpnt_mstr pm,
gva_s01.grp_mstr gm,
gva_s01.third_prty_admin_mstr tpam
WHERE gm.cntrct_id = 'CNTRCTID0000'
AND gm.third_prty_admin_id = 0
AND gm.third_prty_admin_id = tpam.third_prty_admin_id
AND gm.cntrct_id = pm.cntrct_id
--This is where I can't figure it out
AND SUBSTRING(pm.partcpnt_id, -4) = '1234'

In Oracle, you want SUBSTR( string, position ) and a negative position will count from the end of the string. So:
AND SUBSTR(pm.partcpnt_id, -4) = '1234'
Or you can use LIKE:
AND pm.partcpnt_id LIKE '%1234'

Related

SQL 2-table query results need to be concatenated

My current SQL:
SELECT B.MESSAGENO, B.LINENO, B.LINEDATA
FROM BILL.MESSAGE AS B, BILL.ACTIVITYAS A
WHERE A.MSGNO = D.MESSAGENO AND A.FUPTEAM = 'DBWB'
AND A.ACTIVITY = 'STOPPAY' AND A.STATUS = 'WAIT'
AND A.COMPANY = D.COMPANY
MESSAGENO LINENO LINEDATA
1234567 1 CHEQUE NO : 9999999 RUN NO : 55555
1234567 2 DATE ISSUED: 12/25/2020 AMOUNT : 710.51
1234567 3 PAYEE : LASTNAME, FIRSTNAME
1234567 4 ACCOUNT NO : 12345-67890
1234567 5 USER : USERNAME
there are 550 sets of 5 lines per MESSAGENO
What I am trying to figure out is how I can get something like where LINENO = 1, concatenate LINEDATA so I just get 9999999 as checkno, where LINENO = 2, concatenate LINEDATA so I get 710.51 as amount, where LINENO = 3, concatenate LINEDATA so I get LASTNAME, FIRSTNAME as payee, where LINENO = 4, concatenate LINEDATA so I get LASTNAME, FIRSTNAME as payee, and lastly, the same thing for USERNAME.
I just cannot seems to conceptualize this. Every time I try, my brain starts turning into macaroni. Any help is appreciated.
UPDATED ANSWER, extracts all fields from stored strings:
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=e22d26866053ea6088aa78dc23c4d809
Check this fiddle.
It uses a SUBSTRING_INDEX in the internal query to split the fields at the : or by a combination of : and " ". I used the two spaces because I wasn't sure what your actual whitespace was, and when I copied the data out of your post it was just spaces.
Then MAX is used in the outer query to get everything on one line, grouping by the messageNo. Since some lines have two pieces of data to extract, a second string parser was added. Here is the code from the fiddle, for reference. In this case, the table test was created from the output of OP's initial query, since I didn't have enough info to build both tables and completely recreate his output.
SELECT MESSAGENO,
MAX(if(LINENO = 1, extractFirst, null)) as checkNo,
MAX(if(LINENO = 1, extractLast, null)) as runNo,
MAX(if(LINENO = 2, extractFirst, null)) as issued,
MAX(if(LINENO = 2, extractLast, null)) as amount,
MAX(if(LINENO = 3, extractFirst, null)) as payee,
MAX(if(LINENO = 4, extractFirst, null)) as accountNo,
MAX(if(LINENO = 5, extractFirst, null)) as username
FROM (
SELECT MESSAGENO, LINENO,
trim(substring_index(substring_index(LINEDATA, ": ", -2), " ", 1)) as extractFirst,
trim(substring_index(LINEDATA, ":", -1)) as extractLast
FROM test
) z
GROUP BY MESSAGENO
Again, you will be much better off to alter your tables so that you can use simpler queries, as shown in the last fiddle.
===============================================
ORIGINAL ANSWER (demo of string parsing, suggestion for data model change)
You can achieve this with some string parsing BUT ABSOLUTELY DO NOT unless you have no choice. The reason you are having trouble is because your data shouldn't be stored this way.
I've included a fiddle incorporating this case statement and substring_index to extract the data. I have assumed mySQL 8 because you didn't specify SQL version.
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=068a49a2c819c08018691e54bcdf91e5
case when LINENO = 1 then trim(substring_index(substring_index(LINEDATA, "RUN NO", 1), ":", -1))
else trim(substring_index(LINEDATA, ":", -1)) end
as LDATA
See this fiddle for the full statement. I have just inserted the data from your join into a test table, instead of trying to recreate all your tables, since I don't have access to all the data I would need for that. In future, set up a fiddle like this one with some representative data and the SQL version, and it will be easier for people to help you.
=========================================
I think this is a better layout for you, with all data stored as the proper type and a field defined for each one and the extra text stripped out:
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=11d52189b005740cdc53175466374635

LAST REG From a Query SQL

I'm trying to get the last record from this query but i don't know how to do it. I used ROW_NUMBER but my program (Protheus ADVPL) don't have resources to get the last line from a query
SELECT ROW_NUMBER() OVER (ORDER BY B1_MASTER, B1_COD) AS ID,
B1_COD,
B1_DESC,
B1_CATEG,
B1_MASTER,
A2_COMPRAD,
ISNULL((SELECT Sum(C6_QTDVEN * C6_PRCVEN)
FROM SC6010 SC6,
SF4010 SF4,
SC5010 SC5
WHERE C6_FILIAL = '01'
AND C6_PRODUTO = B1_COD
AND SC6.D_E_L_E_T_ <> '*'
AND C5_FILIAL = C6_FILIAL
AND C5_NUM = C6_NUM
AND C5_EMISSAO BETWEEN '20160401' AND '20160404'
AND C5_TIPO = 'N'
AND C5_MODAL = '2'
AND SC5.D_E_L_E_T_ <> '*'
(query have 106 lines so i ll not put everything)
I need the total records in a column, like this:
Tabela
What can i do?
Tks
You can use MAX(field) too.
But, you're using ADVPL, so you could use dbSeek instead to find the last RECNO.
So, using "work area" you can find the last record with this:
TRB->(RECCOUNT())
I changed ROW_NUMBER to ##ROWCOUNT and it works! Tks all

Dynamic appending constraints in SQL query

I have this SQL
SELECT devudp1.deviceoid,devudp1.valueType
FROM DeviceUdpValues devUDP1
WHERE devudp1.udpname='TestUDP'
and <<either bdvalue or string value based on user selected value datatype>>
Here in the Query based on the devudp1.valueType I want to append below attribute
If the valueType is 3 then I want to append my above select clause with devudp1.bdvalue ='10', else it should be appended by devudp1.bdvalue = 'Hello'
So the above query when valueType is 3 will look like
SELECT devudp1.deviceoid,devudp1.valueType
FROM DeviceUdpValues devUDP1
WHERE devudp1.udpname='TestUDP'
AND devudp1.bdvalue = '10'
else it will look like
SELECT devudp1.deviceoid,devudp1.valueType
FROM DeviceUdpValues devUDP1
WHERE devudp1.udpname='TestUDP'
AND devudp1.stringValue = 'Hello'
Can anyone suggest me how to put this logic in place
Try this:
SELECT devudp1.deviceoid,devudp1.valueType
FROM DeviceUdpValues devUDP1
WHERE devudp1.udpname='TestUDP'
AND (
(<USER-SELECTED-VALUE> = 3 AND devudp1.bdvalue ='10') OR
(<USER-SELECTED-VALUE> <> 3 AND devudp1.stringvalue ='Hello')
)

How to make this SQL query more efficient and how to do more

I have a sqlite3 database in which I have corrupt data. I qualify "corrupt" with the following characteristics:
Data in name, telephone, latitude, longitude columns is corrupt if: The value is NULL or "" or length < 2
Data in address column is corrupt if The value is NULL or "" or number of words < 2 and length of word is <2
To test this I wrote the following script in Ruby:
require 'sqlite3'
db = SQLite3::Database.new('development.sqlite3')
db.results_as_hash = true;
#Checks for empty strings in name, address, telephone, latitude, longitude
#Also checks length of strings is valid
rows = db.execute(" SELECT * FROM listings WHERE LENGTH('telephone') < 2 OR LENGTH('fax') < 2 OR LENGTH('address') < 2 OR LENGTH('city') < 2 OR LENGTH('province') < 2 OR LENGTH('postal_code') < 2 OR LENGTH('latitude') < 2 OR LENGTH('longitude') < 2
OR name = '' OR address = '' OR telephone = '' OR latitude = '' OR longitude = '' ")
rows.each do |row|
=begin
db.execute("INSERT INTO missing (id, name, telephone, fax, suite, address, city, province, postal_code, latitude, longitude, url) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", row['id'], row['name'], row['telephone'], row['fax'], row['suite'], row['address'], row['city'], row['province'],
row['postal_code'], row['latitude'], row['longitude'], row['url'] )
=end
id_num = row['id']
puts "Id = #{id_num}"
corrupt_name = row['name']
puts "name = #{corrupt_name}"
corrupt_address = row['address']
puts "address = #{corrupt_address}"
corrupt_tel = row['telephone']
puts "tel = #{corrupt_tel}"
corrupt_lat = row['latitude']
puts "lat = #{corrupt_lat}"
corrupt_long = row['longitude']
puts "lat = #{corrupt_long}"
puts '===end===='
end
#After inserting the records into the new table delete them from the old table
=begin
db.execute(" DELETE * FROM listings WHERE LENGTH('telephone') < 2 OR LENGTH('fax') < 2 OR LENGTH('address') < 2 OR
LENGTH('city') < 2 OR LENGTH('province') < 2 OR LENGTH('postal_code') < 2 OR LENGTH('latitude') < 2 OR LENGTH('longitude') < 2
OR name = '' OR address = '' OR telephone = '' OR latitude = '' OR longitude = '' ")
=end
This works but Im new to Ruby and DB programming. So I would welcome any suggestions to make this query better.
The ultimate goal I have is to run a script on my database which tests the validity of data in it and if there are some data that are not valid they are copied to a different table and deleted from the 1st table.
Also, I would like to add to this query a test to check for duplicate entries.
I qualify an entry as duplicate if more than 1 rows share the same name and the same address and the same telephone and the same latitude and the same longitude
I came up with this query but Im not sure if its the most optimal:
SELECT *
FROM listings L1, listings L2
WHERE L1.name = L2.name
AND L1.telephone = L2.telephone
AND L1.address = L2.address
AND L1.latitude = L2.latitude
AND L1.longitude = L2.longitude
Any suggestions, links, help would be greatly appreciated
Your first query doesn't have any significant performance problem. It will run with a seq scan evaluating your "is corrupt" predicate. The check for == '' is redundant with length(foo) < 2 as length('') is < 2. You have a bug where you quoted the field names in your length() calls, so you'll be evaluating the length of the literal field name instead of the value of the field. You have also failed to test for NULL which is a value distinct from ''. You can use the coalesce function to convert NULL to '' and capture NULLS with the length check. You also don't seem to have addressed the special word based rule for address. This later is trouble unless you extend sqlite with a regexp function. I suggest approximating it with LIKE or GLOB.
Try this alternative:
SELECT * FROM listings
WHERE LENGTH(coalesce(telephone,'')) < 2
OR LENGTH(coalesce(fax,'')) < 2
OR LENGTH(coalesce(city,'')) < 2
OR LENGTH(coalesce(province,'')) < 2
OR LENGTH(coalesce(postal_code,'')) < 2
OR LENGTH(coalesce(latitude,'')) < 2
OR LENGTH(coalesce(longitude,'')) < 2
OR LENGTH(coalesce(name,'')) < 2
OR LENGTH(coalesce(address,'')) < 5
OR trim(address) not like '%__ __%'
You find duplicates query doesn't work, since there's always at least one record to match when self joining on equality. You need to exclude the record under test on one side of the join. Typically this can be done by excluding on primary key. You haven't mentioned if the table has a primary key, but IIRC sqllite can give you a proxy for one with ROWID. Something like this:
SELECT L1.*
FROM listings L1
where exists (
select null
from listings L2
where L1.ROWID <> L2.ROWID
AND L1.name = L2.name
AND L1.telephone = L2.telephone
AND L1.address = L2.address
AND L1.latitude = L2.latitude
AND L1.longitude = L2.longitude
)
BTW, while you stressed efficiency in your question, it's important to make your code correct before you worry about efficiency.
I think you're doing overprocessing. As the length of the string '' is 0 then it matches the condicion length('') < 2. So, you don't need to check if a field is equal to '' as it has already been filtered by the conditions on the length function.
However, I don't see how you're checking for null values. I'd replace all the aField = '' with aField is null.

SQL CONCAT IF Statement?

Morning All,
Im not to sure how i need to solve my following query... I have the following query which pulls back the desired records in SQL server...
SELECT agenda.AgendaItemNumber,Agenda.AgendaName, AgendaType.AgendaTypeDescription, userdetails.fullName
FROM Agenda
JOIN AgendaType ON AgendaType.AgendaTypeID=Agenda.AgendaTypeID
JOIN UserDetails ON Agenda.AgendaID = Userdetails.AgendaID
WHERE agenda.AgendaTypeID = '2'
AND AgendaItemNumber = AgendaItemNumber
AND AgendaName = AgendaName
AND AgendaTypeDescription = AgendaTypeDescription
AND AgendaItemNumber >= '3'
The above query works but i need to enhance this slightly. It pulls back the following results, which essentially are duplicate records except for the 'fullname' column...
What i would like to do is be able to add some extra code to this query so that when i run the query i am able to display one record for each 'AgendaItemNumber' and for it to concat both of the fullnames for this record. However i have additional AgendaItemsNumbers in this table that only have 1 x user fullname assigned to them. its just these few records within the image file i need to do something clever with.
Maybe there is a better way to complete this task?
Many thanks in advance. Any queries please dont hesitate to ask.
Regards
Betty
SELECT agenda.AgendaItemNumber,
Agenda.AgendaName,
AgendaType.AgendaTypeDescription,
STUFF(( SELECT ';' + FullName
FROM UserDetails
WHERE UserDetails.AgendaID = Agenda.AgendaID
FOR XML PATH('')
), 1, 1, '') AS fullName
FROM Agenda
INNER JOIN AgendaType
ON AgendaType.AgendaTypeID=Agenda.AgendaTypeID
INNER JOIN UserDetails
ON Agenda.AgendaID = Userdetails.AgendaID
WHERE agenda.AgendaTypeID = '2'
AND AgendaItemNumber = AgendaItemNumber
AND AgendaName = AgendaName
AND AgendaTypeDescription = AgendaTypeDescription
AND AgendaItemNumber >= '3'
ADENDUM
The XML extension in SQL-Server allows you to concatenate multiple rows into a single row. The actual intention of the extension is so you can output as XML (obviously), but there are some nifty tricks that are byproducts of the extensions. In the above query, if there were a column name in the subquery (FullName) it would output as <FullName>Joe Bloggs1</FullName><FullName>Joe Bloggs2</FullName>, because there is no column name it simply concatenates the rows (not forming proper XML). The PATH part allows you to specify an additional node, for example if you use PATH('Name') in the above you would get <Name>;Joe Bloggs</Name><Name>;Joe Bloggs2</Name> If you combine Path with a column name you would get Joe Bloggs.
Finally the STUFF just removes the semicolon at the start of the list.