MS Access - Query - unique values - ms-access

I have a query where fields are as follows:
UniqueID | RefNum | FirstName | Surname | Aim |.....
UniqueID - is a unique field (no duplicates)
RefNum - contains duplicates
What I'm trying to do is to create a new query (based on the above or amend this one) to extract only records with unique RefNum (remove duplicates from the RefNum field)
The way I did it was select 'Group By' RefNum in the Query Design View and selecting 'First' for the rest of the fields. It achieves what I need.
The problem is that if I switch to the Datasheet View (and subsequently export it to excel to be sent out) the field names are 'FirstOfUniqueID', 'FirstofFirstName', 'FirstOfSurname', etc. Is there a way of keep the original field names (not prefixing them with 'FirstOf') or is there another way of achieving it?

The query designer automatically assigns an alias for a field expression which is based on an aggregate function. So, if you switch from Design View to SQL View for your query, you will see something like this included in the SELECT field list ...
First(FirstName) AS FirstOfFirstName
You can change the alias to something else, and you have a lot of flexibility. However, at least in some cases, when you attempt to re-use the base field name as the alias, Access complains about a "circular reference". I don't know whether that would happen here, but you can try it like this ...
First(FirstName) AS [FirstName]
Whether or not that does what you want, I'll suggest you consider a different query strategy which almost completely avoids the field name alias issue. First test this query to confirm it returns suitable RefNum/UniqueID pairs. If your base query is named Query1 ...
SELECT q1.RefNum, Min(q1.UniqueID) AS MinOfUniqueID
FROM Query1 AS q1
GROUP BY q1.RefNum
Assuming that one returns the correct rows, join it back to the base query to select only the base query rows which match ...
SELECT q.*
FROM
Query1 AS q
INNER JOIN
(
SELECT q1.RefNum, Min(q1.UniqueID) AS MinOfUniqueID
FROM Query1 AS q1
GROUP BY q1.RefNum
) AS sub
ON q.UniqueID = sub.MinOfUniqueID

If you switch the view of your query to SQL view, you will see for example AS FirstOfFirstName.
Change this to AS FirstName and follow this on the other fields.
If you prefer doing this in design view you can do so by adding FirstName:
in front of the fieldname and so on:

Related

How do I search for multiple rows in a table or search from concatenated values?

I'm using MySQL and here is a DB table called "profile".
Then my project requires me to perform some kind of "advanced search" as below on the profiles. I can choose 'match any' or 'match all', then the field (Full Name, Email, etc), condition (is, is not, contains, does not contain, etc) and type the values.
Firstly, I tried to concatenate the fields and here is the table.
Then to achieve the advanced search, I used the where clause followed by like, and/or as follows.
SELECT (GROUP_CONCAT(`field_name`,'=',`field_value`)) AS `profile_values`,
FROM `test`.`profiles`
where `profile_values` like 'Full Name=%Bryan%' or `profile_values` like 'Phone=%123456789%'
group by `user_id`
This query will give 2 results (user_id = 1 and 3). If I need to match all the searching fields, I simply needed to change the 'or' to 'and' in the query.
However, 3 results (user_id=1,2,3) instead of 2 (user_id=2,3) will be shown if I changed the query like below:
..... where profile_values like 'Full Name=%B%'
It seems that the query will search for 'b' in any part of the profile_values.
Any advice on how to modify the search query? Or should I not concatenate the data?
The query
FullName=%B%
will look for anything after FullName that includes a B, you are correct in that.
You need to structure your query better.
You don't need to create that second table with the concatenated field.
Instead write a query that passes the parameters into a query something like this:
SELECT * FROM profile WHERE
FullName LIKE '%B%'
AND Email LIKE '%xyz%'
The code structure for creating the query will be more complex and you'll need to include AND and OR but it'll solve your LIKE problem and remove the need for the second table.

MS Access IN operator not returning rows

Using an MS Access database (I know), the query below
SELECT * FROM PageImage WHERE (PageImage.Rooms) In ('1');
only returns rows when the 'PageImage.Rooms' column has a single value like 1
The actual data stored in the 'PageImage.Rooms' column is a comma separated list like 1,2,3 stored as a string, but no data is returned when this is the case.
From the above query & referring to the image below, only row 342 is returned, when I expect 341,342,343 to be returned
Given that PageImage.Rooms stores a genuine comma-delimited list as you specified in a comment (i.e. it is not an Access 2007+ multi-value field), you really have created a bit of a mess. A table containing such a column is for that reason completely un-normalized (not even in first normal form). Query and data consistency issues are to be expected.
The usual way to model a many-to-many relationship such as you have would be with a separate table. In this case, however, since the PageImage entity seems to have no data other than its Id, you could do it in the same table. Either way, whichever table models the relationship should have a separate row for each (PageImage.Id, Room) pair, and those pairs should be a composite key for the table.
With the structure you have now, the query you seem to want could be implemented like this:
SELECT * FROM PageImage
WHERE
PageImage.Rooms = '1'
OR PageImage.Rooms LIKE '*,1'
OR PageImage.Rooms LIKE '1,*'
OR PageImage.Rooms LIKE '*,1,*'
;
As Gord Thompson observed, that could be expressed more compactly, and perhaps more efficiently, as
SELECT * FROM PageImage
WHERE (',' & PageImage.Rooms & ',') LIKE '*,1,*'
;
As you can tell, it gets worse if you want to compare against multiple room numbers in the same query.
Bottom line: strings containing comma-delimited lists are just strings as far as the database is concerned.
I decided to scrap the multi-value string field & go down the correct route of creating a mappings table
The IN operation is the same action as performing an expanded OR statement where
SELECT * FROM PageImage WHERE (PageImage.Rooms) In ('1','2');
is the same as
SELECT * FROM PageImage WHERE PageImage.Rooms = '1' OR PageIMage.Rooms = '2';
Dealing with a partial match requires the LIKE keyword so SELECT * FROM PageImage WHERE PageImage.Rooms LIKE '*1*'; will return any field that has a one in any position within the field.

Subquery for fetching table name

I have a query like this :
SELECT * FROM (SELECT linktable FROM adm_linkedfields WHERE name = 'company') as cbo WHERE group='BEST'
Basically, the table name for the main query is fetched through the subquery.
I get an error that #1054 - Unknown column 'group' in 'where clause'
When I investigate (removing the where clause), I find that the query only returns the subquery result at all times.
Subquery table adm_linkedfields has structure id | name | linktable
Currently am using MySQL with PDO but the query should be compatible with major DBs (viz. Oracle, MSSQL, PgSQL and MySQL)
Update:
The subquery should return the name of the table for the main query. In this case it will return tbl_company
The table tbl_company for the main query has this structure :
id | name | group
Thanks in advance.
Dynamic SQL doesn't work like that, what you created is an inline-view, read up on that. What's more, you can't create a dynamic sql query that will work on every db. If you have a limited number of linktables you could try using left-joins or unions to select from all tables but if you don't have a good reason you don't want that.
Just select the tablename in one query and then make another one to access the right table (by creating the query string in php).
Here is an issue:
SELECT * FROM (SELECT linktable FROM adm_linkedfields WHERE name = 'company') as cbo
WHERE group='BEST';
You are selecting from DT which contains only one column "linktable", then you cant put any other column in where clause of outer block. Think in terms of blocks the outer select is refering a DT which contains only one column.
Your problem is similar when you try to do:
create table t1(x1 int);
select * from t1 where z1 = 7; //error
Your query is:
SELECT *
FROM (SELECT linktable
FROM adm_linkedfields
WHERE name = 'company'
) cbo
WHERE group='BEST'
First, if you are interested in cross-database compatibility, do not name columns or tables after SQL reserved words. group is a really, really bad name for a column.
Second, the from clause is returning a table containing a list of names (of tables, but that is irrelevant). There is no column called group, so that is the problem you are having.
What can you do to fix this? A naive solution would be to run the subquery, run it, and use the resulting table name in a dynamic statement to execute the query you want.
The fundamental problem is your data structure. Having multiple tables with the same structure is generally a sign of a bad design. You basically have two choices.
One. If you have control over the database structure, put all the data in a single table, linktable for instance. This would have the information for all companies, and a column for group (or whatever you rename it). This solution is compatible across all databases. If you have lots and lots of data in the tables (think tens of millions of rows), then you might think about partitioning the data for performance reasons.
Two. If you don't have control over the data, create a view that concatenates all the tables together. Something like:
create view vw_linktable as
select 'table1' as which, t.* from table1 t union all
select 'table2', t.* from table2 t
This is also compatible across all databases.

MySQL IN() clause multiple returns

I have a special data environment where I need to be returned data in a certain way to populate a table.
This is my current query:
SELECT
bs_id,
IF(bs_board = 0, 'All Boards', (SELECT b_name FROM certboards WHERE b_id IN (REPLACE(bs_board, ';', ',')))) AS board
FROM boardsubs
As you can see I have an if statement then a special subselect.
The reason I have this is that the field bs_board is a varchar field containing multiple row IDs like so:
1;2;6;17
So, the query like it is works fine, but it only returns the first matched b_name. I need it to return all matches. For instance in this was 1;2 it should return two boards Board 1 and Board 2 in the same column. Later I can deal with adding a <br> in between each result.
But the problem I am dealing with is that it has to come back in a single column both name, or all names since the field can contain as many as the original editor selected.
This will not work the way you're thinking it will work.
Let's say bs_board is '1;2;3'
In your query, REPLACE(bs_board, ';', ',') will resolve to '1,2,3', which is a single literal string. This makes your final subquery:
SELECT b_name FROM certboards WHERE b_id IN ('1,2,3')
which is equivalent to:
SELECT b_name FROM certboards WHERE b_id = '1,2,3'
The most correct solution to the problem is to normalize your database. Your current system or storing multiple values in a single field is exactly what you should never do with an RDBMS, and this is exactly why. The database is not designed to handle this kind of field. You should have a separate table with one row for each bs_board, and then JOIN the tables.
There are no good solutions to this problem. It's a fundamental schema design flaw. The easiest way around it is to fix it with application logic. First you run:
SELECT bs_id, bs_board FROM boardsubs
From there you parse the bs_board field in your application logic and build the actual query you want to run:
SELECT bs_id,
IF(bs_board = 0, 'All Boards', (SELECT b_name FROM certboards WHERE b_id IN (<InsertedStringHere>) AS board
FROM boardsubs
There are other ways around the problem, but you will have problems with sorting order, matching, and numerous other problems. The best solution is to add a table and move this multi-valued field to that table.
The b_id IN (REPLACE(bs_board, ';', ',')) will result in b_id IN ('1,2,6,7') which is different from b_id IN (1,2,6,7) which is what you are looking for.
To make it work either parse the string before doing the query, or use prepared statements.

How to select one parameter from a SQL query which yields a no. of parameter?

I am having a sql query
select devices.id, devices.type_designator_id, devices.color, devices.status,
devices.device_build, users.username
from devices,users
where
devices.user_id=users.id and devices.user_id=1608
ORDER BY devices.id;
Now it will give me 6 output from two tables devices and users.
Now I want to extract only one output from above query (without changing the anything) type_designator_id, to put it as a parameter for next sql query with different table.
Say new table is Type_designators with a parameter name id, which is same as the type_designator_id from the previous query.
You could consider creating a view defined by the query you've shown above, and using that view in your new query.