Access Query Memo field truncation due to "distinct" - ms-access

I am having problems running a query without either truncating the note field in NotesTbl or returning repeated entries.
UID is not unique for AccessTbl. When I leave out "distinct" notes will return multiple times because I am joining with AccessTbl on a non-distinct condition. When I use distict, the note field is trunctated because it is a memo field.
Here is my query:
SELECT DISTINCT NotesTbl.pin, NotesTbl.noteid, NotesTbl.note, NotesTbl.date,
AccessTbl.affiliation, AccessTbl.name
FROM NotesTbl
LEFT JOIN AccessTbl
ON NotesTbl.UID = AccessTbl.UID
WHERE PIN = #pin#
AND UID = '#uid#'
ORDER BY NotesTbl.DATE DESC

The Access database engine normally determines uniqueness of text ('String') data using only the first 255 characters and that is why DISTINCT and GROUP BY will truncate.
This suggestion is a bit long winded but it does work: split the MEMO into chunks of 255 characters, do use DISTINCT on the chunks, then concatenate them back together again e.g. (Access database engine ANSI-92 Query Mode syntax i.e. parens for subqueries):
SELECT DT2.MyMemoCol_1 & DT2.MyMemoCol_2 AS MyMemoCol
FROM (
SELECT DISTINCT DT1.MyMemoCol_1, DT1.MyMemoCol_2
FROM (
SELECT MID(MyMemoCol, 1, 255) AS MyMemoCol_1,
MID(MyMemoCol, 256, 255) AS MyMemoCol_2
FROM Test1
) AS DT1
) AS DT2;
A comment has been posted:
Breaking the memo down in to
255-character chunks is entirely
unnecessary. You can simply sort on
Left(MyMemoCol, 8192) or some other
appropriately chosen value for the
field length returned.
Well, in my testing this doesn't work at all. Quick repro:
CREATE TABLE Test1 (MyMemoCol MEMO NOT NULL);
INSERT INTO Test1 (MyMemoCol) VALUES (STRING(300, 'A'));
INSERT INTO Test1 (MyMemoCol) VALUES (STRING(300, 'A') & STRING(5, 'X'));
INSERT INTO Test1 (MyMemoCol) VALUES (STRING(300, 'A'));
SELECT LEFT$(MyMemoCol, 8192)
FROM Test1
GROUP
BY LEFT$(MyMemoCol, 8192);
Tested using the SQL view of a Access2007 .accdb ACE engine Query object in SQL-92 Query Mode, the query returns a single row (incorrect) whose value has been truncated at 255 characters (incorrect).
The earlier 'chunking' query returns two rows (correct) without truncation (correct).

I found a solution that seems to work. I used a "group by" to force distinctness on the PIN and NoteID. I tried to exclude the note from distinctness comparissons by using First() to avoid truncation.
SELECT NotesTbl.pin, NotesTbl.noteid, First(NotesTbl.note) as notebody, NotesTbl.date,
AccessTbl.affiliation, AccessTbl.name
FROM NotesTbl
LEFT JOIN AccessTbl
ON NotesTbl.UID = AccessTbl.UID
WHERE PIN = #pin#
AND UID = '#uid#'
GROUP BY pin,affiliation,name,date,noteid
ORDER BY NotesTbl.DATE DESC

Edit: --Removed first suggestion--
...
Another method would be to break your request into two queries: One that refines AccessTbl so that UID is unique within the query, and another that joins NotesTbl to the qryAccessTblUnique query you just created.

You could also filter the query object on the CF end to remove (or ignore) responses which have already been displayed. Not efficient, but if you are using Acess, I'm guessing heavy traffic is not a huge issue.
Something like:
<cfoutput query="notes">
<cfset diplay="true">
<cfloop from="1" to="#notes.currentrow-1#">
<cfif note neq notes.note[i]>
<cfset display="false">
</cfif>
</cfloop>
<cfif display>
#note#
</cfif>
</cfoutput>
For large numbers of returns n, this is going to be ugly at O(n^2), but for small n, it should give you what you want.

Related

Group rows but keep values where not null

I am trying to group rows in MySQL but end up with a wrong result.
My DB looks like this:
I'm using this query:
SELECT
r_id, va_id,va_klasse,va_periode,
1va_mer,1va_hjem,1va_mot,1va_bil,1va_fit,1va_hand,1va_med,1va_fra,
2va_mer,2va_hjem,2va_trae,2va_bil,2va_sty,2va_mus,2va_med,2va_fra,
3va_mer,3va_hjem,3va_mot,3va_bil,3va_pima,3va_nat,3va_med,3va_fra,
va_lock, va_update
FROM o6hxd_valgfag
WHERE va_klasse IN('7A','7B','7C','8A','8B','8C','9A','9B','9C')
GROUP BY va_id
ORDER BY va_klasse,va_name
This produces a wrong result, where one row is returned with only the first three numbers 123 and not the ones from row two and three.
What I would like is a result where the numbers 123, 321 and 132 are gathered in one line.
I can explain more detailed if this isn't sufficient.
If across those fields there should only be ever one value, you should really have them all in the same record and go about fixing it to insert and update the same record.
Ie I am aware that you database isn't designed correctly
However
To dig you out, you could give this a crack, I suppose.
SELECT
r_id, va_id,va_klasse,va_periode,
MAX(1va_mer),MAX(1va_hjem),MAX(1va_mot),MAX(1va_bil),MAX(1va_fit),MAX(1va_hand),MAX(1va_med),MAX(1va_fra),
MAX(2va_mer),MAX(2va_hjem),MAX(2va_trae),MAX(2va_bil),MAX(2va_sty),MAX(2va_mus),MAX(2va_med),MAX(2va_fra),
MAX(3va_mer),MAX(3va_hjem),MAX(3va_mot),MAX(3va_bil),MAX(3va_pima),MAX(3va_nat),MAX(3va_med),MAX(3va_fra),
va_lock, va_update
FROM o6hxd_valgfag
WHERE va_klasse IN('7A','7B','7C','8A','8B','8C','9A','9B','9C')
GROUP BY va_id
ORDER BY va_klasse,va_name
Your query will not work as intended. Think about this use-case:
what if for row1 (r_id =9), the fields 2va_sty, 2va_mus, 2va_med are not empty and has values?
In such case what should your desired output be? It certainly cannot be the numbers 123, 321 and 132 gathered in one line. Group by is usually used if you want to use aggregate functions executed against a certain field value, in your case va_id.
Not a solution to your problem but i think a better query would be like this (because of the not named columns in the group by clause https://dev.mysql.com/doc/refman/5.5/en/group-by-handling.html):
SELECT
aa.r_id, aa.va_id, aa.va_klasse, aa.va_periode,
aa.1va_mer, aa.1va_hjem, aa.1va_mot, aa.1va_bil, aa.1va_fit, aa.1va_hand, aa.1va_med, aa.1va_fra,
aa.2va_mer, aa.2va_hjem, aa.2va_trae, aa.2va_bil, aa.2va_sty,2va_mus, aa.2va_med, aa.2va_fra,
aa.3va_mer, aa.3va_hjem, aa.3va_mot, aa.3va_bil, aa.3va_pima, aa.3va_nat, aa.3va_med, aa.3va_fra,
aa.va_lock, aa.va_update
FROM o6hxd_valgfag AS aa
INNER JOIN (
SELECT va_id
FROM o6hxd_valgfag
GROUP BY va_id
) AS _aa
ON aa.va_id = _aa.va_id
WHERE aa.va_klasse IN ('7A','7B','7C','8A','8B','8C','9A','9B','9C')
ORDER BY aa.va_klasse, aa.va_name;

MySQL Query gets too complex for me

I'm trying to write a MYSQL Query that updates a cell in table1 with information gathered from 2 other tables;
The gathering of data from the other 2 tables goes without much issues (it is slow, but that's because one of the 2 tables has 4601537 records in it.. (because all the rows for one report are split in a separate record, meaning that 1 report has more than 200 records)).
The Query that I use to Join the two tables together is:
# First Table, containing Report_ID's: RE
# Table that has to be updated: REGI
# Join Table: JT
SELECT JT.report_id as ReportID, REGI.Serienummer as SerialNo FROM Blancco_Registration.TrialTable as REGI
JOIN (SELECT RE.Value_string, RE.report_id
FROM Blancco_new.mc_report_Entry as RE
WHERE RE.path_id=92) AS JT ON JT.Value_string = REGI.Serienummer
WHERE REGI.HardwareType="PC" AND REGI.BlanccoReport=0 LIMIT 100
This returns 100 records (I limit it because the database is in use during work hours and I don't want to steal all resources).
However, I want to use these results in a Query that updates the REGI table (which it uses to select the 100 records in the first place).
However, I get the error that I cannot select from the table itself while updateing it (logically). So I tried selecting the select statement above into a temp table and than Update it; however, then I get the issue that I get to much results (logically! I only need 1 result and get 100) however, I'm getting stuck in my own thougts.. I ultimately need to fill the ReportID into each record of REGI.
I know it should be possible, but I'm no expert in MySQL.. is there anybody that can point me into the right direction?
Ps. fixing the table containing 400k records is not an option, it's a program from an external developer and I can only read that database.
The errors I'm talking about are as follows:
Error Code: 1093. You can't specify target table 'TrialTable' for update in FROM clause
When I use:
UPDATE TrialTable SET TrialTable.BlanccoReport =
(SELECT JT.report_id as ReportID, REGI.Serienummer as SerialNo FROM Blancco_Registration.TrialTable as REGI
JOIN (SELECT RE.Value_string, RE.report_id
FROM Blancco_new.mc_report_Entry as RE
WHERE RE.path_id=92) AS JT ON JT.Value_string = REGI.Serienummer
WHERE REGI.HardwareType="PC" AND REGI.BlanccoReport=0 LIMIT 100)
WHERE TrialTable.HardwareType="PC" AND TrialTable.BlanccoReport=0)
Then I tried:
UPDATE TrialTable SET TrialTable.BlanccoReport = (SELECT ReportID FROM (<<and the rest of the SQL>>> ) as x WHERE X.SerialNo = TrialTable.Serienummer)
but that gave me the following error:
Error Code: 1242. Subquery returns more than 1 row
Haveing the Query above with a LIMIT 1, gives everything the same result
Firstly, your query seems to be functionally identical to the following:
SELECT RE.report_id ReportID
, REGI.Serienummer SerialNo
FROM Blancco_Registration.TrialTable REGI
JOIN Blancco_new.mc_report_Entry RE
ON RE.Value_string = REGI.Serinummer
WHERE REGI.HardwareType = "PC"
AND REGI.BlanccoReport=0
AND RE.path_id=92
LIMIT 100
So, why not use that?
EDIT:
I still don't get it. I can't see what part of the problem the following fails to solve...
UPDATE TrialTable REGI
JOIN Blancco_new.mc_report_Entry RE
ON RE.Value_string = REGI.Serinummer
SET TrialTable.BlanccoReport = RE.report_id
WHERE REGI.HardwareType = "PC"
AND REGI.BlanccoReport=0
AND RE.path_id=92;
(This is not an answer, but maybe a pointer towards a few points that need further attention)
Your JT sub query looks suspicious to me:
(SELECT RE.Value_string, RE.report_id
FROM Blancco_new.mc_report_Entry as RE
WHERE RE.path_id=92
GROUP BY RE.report_id)
You use group by but don't actually use any aggregate functions. The column RE.Value_string should strictly be something like MAX(RE.Value_string) instead.

Is there a way to identify those records not found within a where IN() statement?

From PHP Code $Lines is defined as a list of accessions e.g. 123,146,165,1546,455,155
plant table has sequential records with the highest idPlant (unique identifier) of say 1000.
My simple SQL Query:
SELECT * FROM plant WHERE `plant`.idPlant IN($Lines) order by plant.idPlant;
This brings back row data for '123,146,165' etc.
Is there away to be told that '1546' was not found? (and thus the user probably entered a typo, I can not use a 'confirm all numbers are below X' because in the real data the idPlant may not be sequential and the upper bound will increase during use).
Update:
Looking to get an output that will tell me what Numbers were not found.
You can build up a sub query using unions that returns a list of all your values, then LEFT JOIN against that, checking for NULL in the WHERE clause to find the non matching values.
Basic php for this would be something like this:-
<?php
$sub_array = explode(',', $Lines);
$sub = '(SELECT '.implode(' AS i UNION SELECT ', $sub_array).' AS i) sub0';
$sql = "SELECT sub0.i
FROM $sub
LEFT OUTER JOIN plant
ON plant.idPlant = sub0.i
WHERE plant.idPlant IS NULL";
?>
You can create a temporary table and compare it to the original table. It goes something like this:
CREATE TEMPORARY TABLE IF NOT EXISTS plantIDs (
ID INT(11) NOT NULL UNIQUE,
found INT(11) NOT NULL);
INSERT INTO plantIDs(ID) VALUES (123),(146),(165),(1546),(455),(155);
SELECT plantIDs.ID, COALESCE(plant.name, "Not Found") as PlantName, plant.* FROM plant RIGHT JOIN plantIDs ON plant.idPlant=plantIDs.ID ORDER BY plantIDs.ID;
Assuming you have a field named name inside the table plant, this code will produce a row for each plant and the column named PlantName will contain the name of hte plant or the text "Not Found", ofc you can change the coalesce value to anything that fits your needs.

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.

How do I copy multiple values from within the same table to other values in that same table in SQL?

First of all I'm rather new to SQL and so even though I believe a similar question was asked in this thread ( SQL Query - Copy Values in Same Table ) I literally can't understand it well enough to utilize the information. For that I apologize.
Now, I have a table that looks something like this:
company id | parameter name | parameter title
P | Parameter One | First Parameter
P | Parameter Two | Second Parameter
P | Parameter Three| Third Parameter
W | Parameter One | NULL
W | Parameter Two | NULL
Except that my table obviously has quite a lot of rows. I already went through filling in all the parameter titles where the company id was 'P' and would like to avoid manually doing the same for those with company id 'W'. My question is what SQL statement (this is in Microsoft SQL Server 2008) can I use to copy the values in the column "parameter title" where the company id is 'P' to the values in the same column where the company id is 'W' and both parameter names match up (W has less parameters than P)?
Using the previously linked thread I was able to come up with the following, but it spits out an error and I know it's not done correctly:
UPDATE COMP_PARAMETER_COPY
SET PARAM_TITLE=(SELECT PARAM_TITLE FROM COMP_PARAMETER_COPY P
WHERE P.COMP_ID = 'P' AND P.PARAM_TITLE=PARAM_TITLE)
WHERE COMP_ID='W'
(I'm playing around with a copy of the table instead of the actual table)
The error I get is "Msg 512, Level 16, State 1, Line 1
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
The statement has been terminated."
Thank you for your help and advice,
-Asaf
You need to ensure that your subquery is only returning one result. Right now that error message is telling you that you're getting more than one record returned.
UPDATE W
SET PARAM_TITLE = (
SELECT PARAM_TITLE FROM COMP_PARAMETER_COPY P
WHERE P.COMP_ID = 'P' AND P.PARAM_NAME = W.PARAM_NAME
)
FROM COMP_PARAMETER_COPY W
WHERE W.COMP_ID = 'W'
Try giving the above SQL a whirl. This could still give you more than one result, but without knowing what your table looks like and what the data constraints are it's hard to give you something guaranteed to work.
Try adding the DISTINCT keyword to your query:
UPDATE COMP_PARAMETER_COPY
SET PARAM_TITLE=(SELECT DISTINCT PARAM_TITLE FROM COMP_PARAMETER_COPY P
WHERE P.COMP_ID = 'P' AND P.PARAM_TITLE=PARAM_TITLE)
WHERE COMP_ID='W'