MS Access inner join with inexact matching (wildcard or similar) - ms-access

I have an Access database that I am currently working on. I have 2 tables and I want to combine both tables. The issue I am coming across is that the field I am using to match both tables is not always the same, meaning I will have to use a wildcard and I am not too sure on how to do that.
The names of my two tables are:
ACW,Hold
QMT
Query will have the following fields:
RM Field that is present on both tables.
ACW comes from table ACT,Hold
Avg Hold comes from table ACT,Hold
Score comes from table QMT.
The field I am using is "RM" however, since it is names some of them are first name last name in the first table and last name first on the other table. Also, there is extra characters in some scenarios. Is there a way to accomplish this?
I tried the following with no luck:
SELECT [ACW,Hold].RM, [ACW,Hold].ACW, [ACW,Hold].[Avg Hold], QMT.Score
FROM [ACW,Hold] INNER JOIN QMT ON [ACW,Hold].RM = QMT.RM & "*";

The SQL operator that supports wildcards is the LIKE operator, so your query should use it instead of the = operator:
SELECT [ACW,Hold].RM, [ACW,Hold].ACW, [ACW,Hold].[Avg Hold], QMT.Score
FROM [ACW,Hold] INNER JOIN QMT
ON [ACW,Hold].RM LIKE QMT.RM & "*";
I just tried a similar query in Access 2010 and it seemed to work as expected.
Update
If you need to perform matching that is more sophisticated than a single LIKE comparison can offer then you could also create a VBA function that accepts the two field values as arguments and returns a Boolean value indicating whether or not they match. For example, with a function like
Option Compare Database
Option Explicit
Public Function DoTheyMatch(product As String, ingredient As String) As Boolean
Dim result As Boolean
If product Like ingredient & "*" Then
result = True
ElseIf ingredient = "some special thing" And product = "value to match" Then
result = True
Else
result = False
End If
DoTheyMatch = result
End Function
you could use that function as the ON condition of the JOIN:
SELECT i.Ingredient, i.Supplier, p.Product
FROM Ingredients i INNER JOIN Products p
ON DoTheyMatch(p.Product, i.Ingredient);
I just tried that in Access 2010 and it worked, too.

Related

SELECT statement inside a CASE statement in SNOWFLAKE

I have a query where i have "TEST"."TABLE" LEFT JOINED to PUBLIC."SchemaKey". Now in my final select statement i have a case statement where i check if c."Type" = 'FOREIGN' then i want to grab a value from another table but the table name value i am using in that select statement is coming from the left joined table column value. I've tried multiple ways to get to work but i keep getting an error, although if i hard code the table name it seems to work. i need the table name to come from c."FullParentTableName". Is what i am trying to achieve possible in snowflake and is there a way to make this work ? any help would be appreciated !
SELECT
c."ParentColumn",
c."FullParentTableName",
a."new_value",
a."column_name"
CASE WHEN c."Type" = 'FOREIGN' THEN (SELECT "Name" FROM TABLE(c."FullParentTableName") WHERE "Id" = 'SOME_ID') ELSE null END "TestColumn" -- Need assistance on this line...
FROM "TEST"."TABLE" a
LEFT JOIN (
select s."Type", s."ParentSchema", s."ParentTable", s."ParentColumn", concat(s."ParentSchema",'.','"',s."ParentTable",'"') "FullParentTableName",s."ChildSchema", s."ChildTable", trim(s."ChildColumn",'"') "ChildColumn"
from PUBLIC."SchemaKey" as s
where s."Type" = 'FOREIGN'
and s."ChildTable" = 'SOMETABLENAME'
and "ChildSchema" = 'SOMESCHEMANAME'
) c
on a."column_name" = c."ChildColumn"
Thanks !
In Snowflake you cannot dynamically use the partial results as tables.
You can use a single bound value via identifier to bind a value to table name
But you could write a Snowflake Scripting but it would need to explicitly join the N tables. Thus if you N is fixed, you should just join those.

Use ValueA when JOIN returns a row, otherwise ValueB (like a default)

I have four tables for a form-builder in my databse.
fields (fieldID(PK), typeID, fieldName, ...) - This table is a row by row list of all fields to be in the form
fields_types (typeID(PK), htmlType, ...) - This is a table that links fields to html types (and other settings)
fields_meta (FieldMetaID(PK), FieldID, mName, mValue) - Additional settings for fields, but more specific. A textarea field might have a height attribute, but almost no other field would use that.
fields_tyeps_meta (TypeMetaID(PK), typeID, tmName, tmValue) - Defines what extraneous settings a field can have, and also supplies default values if it's not explicitly set)
So my Query currently looks something like this
SELECT *
FROM Fields F
JOIN Field_Types FT
on FT.FieldID = F.FieldID
LEFT
JOIN Field_Meta FM
on FM.FieldID = F.FieldID
I was wondering if there's a way to join Fields_Types_Meta so that when the row's JOIN to Fields_Meta doesn't return a row (no mValue), it returns tmValue
I realize I can use something like (CASE WHEN mValue = "" THEN tmValue ELSE mValue END) AS UseValue, but I might have fields where I want to allow the value to be set to empty.
Edit: I could probably do something with a subquery and COUNT, using a CASE decision based on that. It might not be the healthiest performance-wise, but this query runs and caches itself til server restart, or until it's told to run again (updates to form design)
It looks like you just want ¢oalesce():
coalesce(FM.mValue, FT.tmValue) as UseValue
When FM.mValue is null, coalesce() returns FT.tmValue instead.
If you have null values in FM that you want to preserve in the result set, then use a case expression instead:
case when FM.FieldID IS NULL THEN FT.tmValue ELSE FM.mValue END as UseValue
This phrases as: when the left join did find a match in FM, use mValue from that row (even if it is null), else use FT.tmValue.

Can I query a record with multiple associated records that fit certain criteria?

I have two tables, Show, Character. Each Show has_many Characters.
class Show < ActiveRecord::Base
has_many :characters
class Character < ActiveRecord::Base
belongs_to :show
What I want to do is return the results of a Show that is associated with multiple Characters that fit certain criteria.
For example, I want to be able to return a list of Shows that have as characters both Batman and Robin. Not Batman OR Robin, Batman AND Robin.
So the query should be something like
Show.includes(:characters).where(characters: {'name = ? AND name = ?', "Batman", "Robin"})
But this returns an error. Is there a syntax for this that would work?
UPDATE
The query
Show.includes(:characters).where('characters.name = ? AND characters.name = ?', "Batman", "Robin")
returns a value of 0, even though there are definitely Shows associated with both Batman and Robin.
Using plain SQL, one solution is :
select s. *
from shows as s
join characters as c1 on (s.id=c1.show_id)
join characters as c2 on (s.id=c2.show_id)
where c1.name='Batman'
and c2.name='Robin';
Using Arel, I would translate as :
Show.joins('join characters as c1 on shows.id=c1.show_id').joins('join
characters as c2 on shows.id=c2.show_id').where('c1.name = "Batman"').where(
'c2.name="Robin"')
So you'll have to get a little fancy with SQL here; especially if you want it to be performant and handle different types of matchers.
select count(distinct(characters.name)) as matching_characters_count, shows.* from shows
inner join characters on characters.show_id = shows.id and (characters.name = 'Batman' or characters.name = 'Robin')
group by shows.id
having matching_characters_count = 2
To translate into ActiveRecord
Show.select('count(distinct(characters.name)) as matching_characters_count, shows.*').
joins('inner join characters on characters.show_id = shows.id and (characters.name = "Batman" or characters.name = "Robin")').
group('shows.id').
having('matching_characters_count = 2')
Then you'd probably do a pass with interpolation and then AREL it up; so you wouldn't be building string queries.
A plain SQL solution using aggregate functions. The SQL statements returns the ID values of the ´shows´ records you are looking for.
SELECT c.show_id
FROM characters c
WHERE c.name IN ('Batman','Robin')
GROUP BY c.show_id
HAVING COUNT(DISTINCT c.name) = 2
You can put this statement into a select_values() call and then grab the shows with the values of the returned array.
I think this must work:
super_heroes = ['Batman', 'Robin']
Show.where(
id: Character.select(:show_id).where(name: super_heroes).group(:show_id)
.having("count(distinct characters.name) = #{super_heroes.size}")
)
I just write the #sschmeck query in a Rails way, as a subquery, and add a super_heroes var to show how it can be scaled.
Reduce the number of entries in the query as soon as possible is basic idea to get a better query in performance.
Show.where("id IN (( SELECT show_id FROM characters WHERE name = 'Batman') INTERSECT (SELECT show_id FROM characters WHERE name = 'Robin'))")
In above query, the 1st sub query gets the 'show_id' for 'Batman', the next one gets the 'show_id' for 'Robin'. In my opinion, just a few characters match the condition after doing 'INTERSECT'. Btw, you can use explain command in 'rails db' for choosing what is better solution.
I think you're looking for this:
Show.includes(:characters).where(characters: {name: ["Batman", "Robin"]})
or
Show.includes(:characters).where('characters.name IN ?', ["Batman", "Robin"]).references(:characters)
The references is required because we're using a string in the where method.
So first of all, Why is your approach is wrong?
So in the "characters_shows" table you can find records that looks like this
show_id name character_id character_name
1 first show 1 batman
2 second 1 batman
1 first show 2 robin
1 first show 3 superman
As you can see there will never be a case where the character name is batman and robin at the same row
Another approach will be something like this
names = ["Batman", "Robin"]
Show.joins(:characters).where(characters: {name: names}).group("shows.id").having("COUNT(*) = #{names.length}")

Test for field length in MySql statement

I am using this part of a sql statement to delete accounts with the specific phone number in a cronjob.php file. How do I alter this statement to test for the length of a specific given field in the user-profies, rather than what I list here as the value being LIKE '%2147483648%'
$strSQL = "DELETE
FROM users,
profile_values
USING profile_values
INNER JOIN users USING(uid)
INNER JOIN profile_fields USING(fid)
WHERE profile_values.uid=users.uid
AND profile_values.value LIKE '%2147483648%'
AND (profile_fields.name = 'profile_phone_number')";
execSQL($strSQL);
If you are testing for length of the data in the field, you can use code like:
... WHERE LENGTH(TRIM(profile_values.value)) = 10 ...
substituting the correct field name. The TRIM function is important to eliminate any leading or trailing spaces which might have crept in to the data.
Be careful using a function like this to automatically delete users. It will be quite indiscriminate.

Allowing Optional Parameters for MySQL Query

I have a search page that has multiple fields that are used to create a refined search. Every field is optional. I'm trying to start crafting my sql query so that it will work given the proper variables but I'm having trouble.
Here is the SQL query I currently have:
SELECT
indicator.indid,
indicator.indicator,
indtype.indtype,
provider.provider,
report.report,
actor.actor
FROM
actor,
indicator,
indtype,
report,
provider
WHERE
indicator.indtypeid = indtype.indtypeid
AND indicator.actorid = actor.actorid
AND indicator.reportid = report.reportid
AND report.providerid = provider.providerid
AND indicator.indicator LIKE '%$indicator%'
AND indicator.indtypeid = $indtypeid;
Whenever I provide an indicator and an indtypeid, the search works just fine. However, when I leave the indtypeid field blank, and have the variable set to * (as its default value), the query returns no results. I've tried playing with the query manually and it doesn't seem to like the * or a % sign. Basically, if only an indicator is specified and no indtypeid is specified, I want to return all indicators for all indtypeids.
I'm sure I'm missing something minor, but I would appreciate any assistance that could be provided. I may be going about this all wrong in the first place.
Try this instead:
SELECT i.indid, i.indicator, it.indtype,
p.provider, r.report, a.actor
FROM actor a
INNER JOIN indicator i ON a.actorid = i.actorid
INNER JOIN indtype it ON i.indtypeid = it.indtypeid
INNER JOIN report r ON i.reportid = r.reportid
INNER JOIN provider p ON r.providerid = p.providerid
WHERE 1 = 1
AND ($indicator IS NULL OR i.indicator LIKE '%$indicator%')
AND ($indtypeid IS NULL OR i.indtypeid = $indtypeid);
So if you pass a $indicator = NULL, then the first condition AND ($indicator IS NULL OR i.indicator LIKE '%$indicator%') will be ignored since it will resolve to True, and the same thing for the second condition.
I've removed other Where condition and replace them with JOINs, and for WHERE 1 = 1 to make the query work fine in case you pass the two variables $indicator and $indtypeid with NULL values for each, in this case it will return all results since 1 = 1 always true.