I've been stuck with this problems for a while, and couldn't get it right yet. Here it is:
I have some tables in my Acces database and some querys. I have a query to select some fields and order by, say, their frequency. This is done for two tables, giving me two ranks. Looks like this (and are sorted DESC, so the higher Frequency is on top):
Table 1 Table 2
Value1 Frequency1 Value2 Frequency2
Table2.Value2 is a subset of Table1.Value1, so I want to match Value2 and Value1 plus Add a Column showing Value2 position (rank) on Table2, so I can compare it's position on Table1 and Table2 ranks.So I'll have something like:
Table3
Value1 Frequency1 Frequency2 PositionTable2
I've searched how to return the line number of a field (couldn't get it to work), and I can put the two tables together and match by Value, but can't get the result I need.
My knowledge of SQL is limited, and I don't understand VB at all, so please be nice :)
Thanks in advance
Edit
Example:
Table 1 Table 2
Name Frequency Name Frequency
Mary 5 Paul 2
John 4 John 1
Paul 3
Output I want:
Table 3
Name Frequency Frequency2 RankIn2
Mary 5 //doesn't appear in table2, freq=null rankin2=null
John 4 1 2 //second line of table2
Paul 3 2 1 //first line in table2
You might do this in two steps:
Step 1 -- Create a query (say, named "Table 2 with Rank") that calculates the rank for Table 2. The SQL might look something like this:
SELECT
[Table 2].[Name],
[Table 2].[Frequency],
Count(*) AS [Rank]
FROM
[Table 2],
[Table 2] AS [Self]
WHERE
[Self].[Frequency]>=[Table 2].[Frequency]
GROUP BY
[Table 2].[Name],
[Table 2].[Frequency];
If there are "ties" in Table 2 (that is, different names with the same frequency), this query will assign the same rank to both. If you don't want this, change the WHERE clause to specify how you want to break ties. For example, in the event of a tie, the WHERE clause...
WHERE
[Self].[Frequency]>[Table 2].[Frequency]
OR
([Self].[Frequency]=[Table 2].[Frequency] AND [Self].[Name]<=[Table 2].[Name])
...will assign the lower numbered rank to the name that comes first the in the alphabet.
Step 2 -- Create another query that joins the first query to Table 1. The SQL might look something like this:
SELECT
[Table 1].[Name],
[Table 1].[Frequency],
[Table 2 with Rank].[Frequency] AS [Frequency2],
[Table 2 with Rank].Rank AS [RankIn2]
FROM
[Table 1] LEFT JOIN [Table 2 with Rank]
ON [Table 1].[Name] = [Table 2 with Rank].[Name]
ORDER BY
[Table 1].[Frequency] DESC;
I would use VBA because Access' limited SQL doesn't understand the rank concept very well. Here are the steps.
Create a 3rd field for Table2 and call it Rank.
Create the following subroutine by pressing Alt+F11, Insert->Module and pasting the following code in the editor window that opens.
Public Sub RankTable()
Dim rs As Recordset, iRank As Integer, strQuery As String
strQuery = "SELECT * FROM Table2 ORDER BY Freq DESC"
Set rs = CurrentDb.OpenRecordset(strQuery, dbOpenDynaset)
rs.MoveFirst
iRank = 1
Do
rs.Edit
rs.Fields(3) = iRank
rs.Update
rs.MoveNext
iRank = iRank + 1
Loop While Not rs.EOF
rs.Close
End Sub
Run the above subroutine. (Remember you have to run this every time there is an update to Table2.)
Create the query
SELECT Table1.Name, Table1.Frequency, Table2.Frequency AS Frequency2, Table2.Rank
FROM Table1
LEFT OUTER JOIN Name ON Table1.Name = Table2.Name
ORDER BY Table2.Frequency
Related
I've created a simple table - table1.
thre is only one talbe (table1)
the table1 has two fields: [table1].[id] and [table1].[method]
the RowSourceType of [table1].[method] is - 'Value list'
the Row Source of [table1].[method] is is ' 1;"A";35;"B";2;"C";3;"D" ' (so two columns).
RowSourceType - Value list
Row Source - 1;"A";35;"B";2;"C";3;"D"
I've populated table1 with rows:
id
method
1
35
2
2
3
1
I'm loking for a query to receive result:
[table1].[id]
[table1].[ method]
1
B
2
C
3
A
(I'd like to avoid to add lookup table)
Thank You in advance.
rgds
You can use Switch:
Select
id,
Switch([method]=1,"A",[method]=35,"B",[method]=2,"C",[method]=3,"D") As MethodCode
From
table1
As #Gustav suggested and perhaps you didn't understand, first step is to create a lookup table, second step is to use that as your RowSource, third step is to build your query
Benefits of this approach is that you don't need to change your Rowsource every time you make a change to the Lookup List
1. Create Lookup Table to match your rowsource
LookupID
LookupValue
1
A
35
B
2
C
3
D
2-a. Change Rowsource of your input field
In Properties | Data
Set Rowsource to
SELECT LookupID, LookupValue FROM LookupTable
Set RowsourceType to
Table/Query
2-b Still in Properties | Format, set up columns and Hide the ID field
ColumnCount =2
ColumnWidths = 0;3cm
3. Build your final query
SELECT Table1.ID, LookupTable.LookupValue
FROM Table1 INNER JOIN LookupTable ON Table1.metod = LookupTable.LookupID;
Results of Query
ID
LookupValue
1
B
2
C
3
A
I have the following table
Code Name Task
aa jones DC
ab dave DC
aca james IF
aca james DC
ab trevor IF
aa jones IF
ag francis DC
ag francis IF
af derek SF
af derek DC
This is a very big table, above is just a quick example.
So, I would like some help finding the code and name that have completed a IF or SF task and a DC task.
I would like it to show where one person has touched both of these tasks. The hierarchy of the tasks is; it comes in as either a SF or IF then someone will do that, then off the back of that we receive a DC task, and I want the ones where it has been completed by the same person, with the same reference number.
I am able to do this in excel with an INDEX MATCH function, but this takes up a tremendous amount of calculation time due to the size of the table.
One way to approach this is using group by with a having. This is a flexible way of expressing these types of conditions:
select code, name
from table t
group by code, name
having sum(case when task = 'DC' then 1 else 0 end) > 0 and
sum(case when task in ('IF', 'SF') then 1 else 0 end) > 0;
Each condition in the having clause counts the number of rows that meet the particular condition. The first, for instance, counts the rows that match 'DC' and takes only the code, name pairs that have at least one such match.
SELECT code,name FROM YOUR_TABLE_NAME WHERE task = 'DC' AND (task = 'IF' OR task = 'SF') GROUP BY name
try this query
Gordon Linoff's query can be made easier under the hypothesys that IF and SF are synonym and cannot be both present for the same Code-Name couple, as the data provided by the OP suggests
SELECT code, name
FROM table t
GROUP BY code, name
HAVING SUM(CASE WHEN task IN ('IF', 'SF', 'DC') THEN 1 ELSE 0 END) = 2;
select code,name from (select distinct code,name from table1 where task='SF' or task='IF') as temp1 inner join (select distinct code as code2,name as name2 from table1 where task='DC') as temp2 on code=code2,name=name2;
I'm assuming that you have the table in table1. The code constructs two tables temp1 and temp2. temp1 contains those codes and names which have been assigned SF and IF. temp2 contains those codes and names which have been assigned DC. Finally, I join the two tables together to find code-name pairs in both tables. This is faster than in Excel because the database engine probably temporarily indexes the columns being joined on.
Actually, you can do this in Excel. You sort the table by code and name, then enter the following formulas (assuming "Code" is in A1):
D2=if(and(A2=A1,B2=B1,D1),true,or(C2="IF",C2="SF"))
E2=if(and(A2=A1,B2=B1,E1),true,C2="DC")
Select these two cells, and double-click the fill-handle (the little square at the bottom right of the selection). Then, with the two columns selected, copy, and then "Paste Special..." > "Values". Then, filter (Alt-D-F-F) for the rows with values in columns D and E being both true. That is the result you want. Select these rows and copy to a new sheet if desired.
Alternatively, you can follow the SQL "group by" solution given by Gordon, so that you do not need to sort: Create two new columns like the above, but:
D1: "D"
E1: "E"
D2=if(or(C2="IF",C2="SF"),1,0)
E2=if(C2="DC",1,0)
Then, "Insert" > "PivotTable", drag "Code" and "Name" to be row labels. Drag "D" to be under Values, click on it, "Value Field Settings...", and then select "Max". Do the same for "E", and then the rows with 1 in both D and E will be the result you want.
I believe similar questions have been asked but I can't quite find a solution that works for me.
I've got a database that I use to sort through digitised books and their pages and I'm trying to sort through several thousand pages that contain maps. Of the two tables I'm using the first lists all the pages in a book and the order they occur in the book, it's got three columns (bookID, pageOrder, pageID), each page has its own row. The second table lists all the places (in a map) that occur on each page, it has two columns (pageID, placeID) if there are multiple places on one page then a new row is added to the table for each place.
What I need to do is create a select statement that gives every pageID/placeID combination a unique number but the numbers must go in the order they appear in the book. In SQL Server I would do this:
SELECT ROW_NUMBER() OVER(ORDER BY bp.bookID, bp.pageOrder, pp.placeID) AS uniqueNumber, pp.pageID, pp.placeID
FROM booksAndPages AS bp INNER JOIN pagesAndPlaces AS pp ON bp.pageID = pp.pageID
Unfortunately, I'm stuck using Access. Ideally I'd like to do it (if possible) with a single SQL statement, similar to the one above but I would also try it using VBA.
Any help is greatly appreciated.
This is the query that you want:
SELECT ROW_NUMBER() OVER (ORDER BY bp.bookID, bp.pageOrder, pp.placeID) AS uniqueNumber,
pp.pageID, pp.placeID
FROM booksAndPages AS bp INNER JOIN
pagesAndPlaces AS pp
ON bp.pageID = pp.pageID;
You can get the same result using a correlated subquery. More complicated and more expensive, but possible:
SELECT (select count(*)
from booksAndPages AS bp2 INNER JOIN
pagesAndPlaces AS pp2
ON bp2.pageID = pp2.pageID
where bp2.bookID < bp.bookID or
(bp2.bookID = bp.bookID and bp2.pageOrder < bp.pageOrder) or
(bp2.bookID = bp.bookID and bp2.pageOrder = bp.pageOrder and
bp2.placeId <= pp.PlaceId
)
) as uniqueNumber,
pp.pageID, pp.placeID
FROM booksAndPages AS bp INNER JOIN
pagesAndPlaces AS pp
ON bp.pageID = pp.pageID;
This assumes that the combination bookId, pageOrder, placeId` is unique.
I know this is an old question, but this was a top search and I haven't seen any other solutions on the internet so I hope this will help others.
My solution works for any dataset regardless of if it has a unique identifier or not.
Add the following VBA code into a module:
Public row as Variant
Function RowNum(dummy) As Integer
row = row + 1
RowNum = row
End Function
Function GetRowNum(dummy) As Integer
GetRowNum = row
End Function
Function ResetRowNum()
row = 0
End Function
Now here's a sample query:
SELECT Table1.Field1, Table1.Field2, RowNum([Field1]) AS RowId,
"Row: "&GetRowNum([Field1]) AS RowText
FROM Table1
You can add any 'ORDER BY' or even 'GROUP BY' if you wish. You can use any field that will be in the query output as the input for RowNum and GetRowNum. Important to note is to only use RowNum for the first time you want the row number and use GetRowNum every time after. This is to prevent one row increasing the counter more than once.
The last thing you need to do is create a macro that runs ResetRowNum and run it after every query you use with this method, or if you're running a series of queries through a macro or VBA, make sure to run ResetRowNum after every query that uses these functions.
Also avoid datasheet view, as it seems to constantly recalculate the formulas when you scroll, making the numbers steadily increase.
Query to sort and/or group
SELECT Table1.Field1,
Table1.SomeDate,
Table1.Field2,
RowNumber([Field1]) AS RowId,
"Row: " & GetRowNum([Field1]) AS RowText
FROM Table1
ORDER BY Table1.Field1, Table1.SomeDate;
Field1 Field2 RowId RowText
James 2 1 Row: 1
James 35 2 Row: 2
James 6 3 Row: 3
James 86 4 Row: 4
James 67 5 Row: 5
James 35 6 Row: 6
Maria 4 1 Row: 1
Maria 54 2 Row: 2
Samuel 46 1 Row: 1
Samuel 32 2 Row: 2
Samuel 7 3 Row: 3
Thomas 43 1 Row: 1
Thomas 65 2 Row: 2
Thomas 5 3 Row: 3
Public StoredRowNumber As Variant
Public OldlastField As Variant
Function RowNumber(TheField) As Integer
If OldlastField = TheField Then
'nada
Else
ResetRowNum
End If
StoredRowNumber = StoredRowNumber + 1
RowNumber = StoredRowNumber
OldlastField = TheField
End Function
Function GetRowNum(TheField) As Integer
GetRowNum = StoredRowNumber
End Function
Function ResetRowNum()
StoredRowNumber = 0
'OldFieldItem = Null
End Function
I am trying to prune an Access table (contam) full of duplicates and concatenated values.
The table looks like this
FederalSiteIdentifier Contam
001 1
001 1, 2
001 1, 2, 3
001 1, 2, 3, 4
002 1
003 1
003 1, 2
003 1, 2, 3
I only want to keep the last - longest - entry for each ID, but cannot figure out the proper way to do this in Access SQL.
After doing some reading I tried this simple code:
SELECT FederalSiteIdentifier, Max(Contam) as MaxCont
FROM contam
ORDER BY FederalSiteIdentifier
which produces an error.
Can anyone help?
I think you want the maximum length of the Contam values for each FederalSiteIdentifier. You will need a GROUP BY clause.
SELECT FederalSiteIdentifier, Max(Len(Contam)) as MaxCont
FROM contam
GROUP BY FederalSiteIdentifier
If that query identifies the rows you wish to keep, save it as qryRows2Keep, then try this DELETE query:
DELECT FROM contam
WHERE
Len(Contam) < DLookup(
"MaxCont",
"qryRows2Keep",
"FederalSiteIdentifier = '" & FederalSiteIdentifier & "'")
I assumed FederalSiteIdentifier is text data type. If it is numeric, discard the single quotes from the DLookup expression.
Please make sure you have backed up your data before attempting this untested suggestion. :-)
MAX is an aggregate function, you can only do these if you use a GROUP BY statement. Access is also very peculiar in the way it can delete records. You can't left join and delete so you have to identify the records you don't want and then delete them.
Create a new column so we can choose the records to keep.
ALTER TABLE contam ADD Keep BIT NOT NULL DEFAULT 0;
Now identify the records and update the table
UPDATE c
SET keep = 1
FROM contam AS c
INNER JOIN (
SELECT FederalSiteIdentifier, Max(Len(Contam)) as MaxCont
FROM contam
GROUP BY FederalSiteIdentifier
ORDER BY FederalSiteIdentifier
) AS maxc
ON c.FederalSiteIdentifier = maxc.FederalSiteIdentifier
AND Len(c.Contam) = maxc.MaxCont;
Note the GROUP BY line...that's what you missed that was giving you an error..
Finally perform the delete
DELETE FROM contam
WHERE keep = 0;
You can now remove the extra column
ALTER TABLE contam DROP COLUMN Keep;
Long winded but there you go.
Is this valid SQL? If yes, could you please tell me what it does?
Select *
from MyFirstTable
order by (select min(somefield)
from MySecondTable
where MyFirstTable.id = MySecondTable.id)
A subselect in an "order by", how is that possible?? In effect this SQL query does not sort by a field, but by some value in a row of a field (min). It does not seem logical so sort by anything else other than a field name. But min(somefield) <> somefield! But, yes, this query works and someone at work who teaches me told me this, and i'm sceptical.
Can you tell me what this means? Or just post an equivilant query?
Thanks!
This query orders MyFirstTable by the minimum value of somefield stored in MySecondTable under the same id.
Here's a quick example:
MyFirstTable
id
1
2
3
MySecondTable
id somefield
1 2
1 4
2 1
3 6
3 4
In the above case, your query would return
id
2
1
3
An equivalent query that may make more sense:
SELECT MyFirstTable.ID, MyFirstTable.A, MyFirstTable.B
FROM MyFirstTable
INNER JOIN MySecondTable ON MyFirstTable.ID = MySecondTable.ID
GROUP BY MyFirstTable.ID, MyFirstTable.A, MyFirstTable.B
ORDER BY MIN(MySecondTable.SomeField)