Linq over multiple datacontexts - linq-to-sql

I have a query, may be poorly written since I'm not sure about this at all, that tries to join (3) tables over (2) contexts, since we have split the database into different logical areas. I get the following error message
"The query contains references to items defined on a different data context."
Dim _db As DBML.XDataContext
_db = New DBML.XDataContext(ConfigurationManager.ConnectionStrings("TempConnectionString").ToString)
Dim _db1 As DBML.YDataContext
_db1 = New DBML.YDataContext(ConfigurationManager.ConnectionStrings("TempConnectionString").ToString)
Dim FAL = (From r In _db.SendingD _
Join e In _db1.Entity On r.USR_FA Equals e.FANo _
And r.USR_HomeD Equals e.OANo _
Join b In _db1.Buildings On e.OBNo Equals b.BuildNo
Where r.USR_FA = FA _
Select b.BuildNo, b.BuildName).ToList()
For Each ab In FAL
list.Add(New ListItem(ab.BuildNo & "-" & ab.BuildName, ab.BuildNo))
Next
Return list
What would I need to do to join the tables to get the results. SendingD is related to Entity on USR_FA = FAno and USR_HomeD = OANo and then Entity is related to Buildings on OBNo = BuildNo

If that's the same server then we prefixed the table name with:
<DatabaseName>.<SchemaName>.<YourTableName>
if they are on a linked server then you have to prefix it with the server name
<ServerName>.<DatabaseName>.<SchemaName>.<YourTableName>

Related

Set default value or ignore Append Query Field that does not exist in MS-Access

I have a series of Excel Tools that work with an Access database (which is really the front end to a series of SharePoint lists). Each Excel Tool has an export function that creates a separate file (CSV) containing data to bring into the database. In the database, the CSV comes in as a local table, and an append query is run to add the contents of the local table to the SharePoint list. I am not fond of the architecture, but due to the robustness of my company's IT Security protocols, this is what I have to do.
There is a list of columns that COULD exist in the CSV, but whether they exist or not depends on the data entered into the workbook. For example, here are all the possible column headings:
kWh Savings
kw Savings
Natural Gas Savings
Water Savings
Fuel Oil Savings
Propane Savings
Depending on the customer/building/end use, some of the fields might not exist. For example, if this is a lighting calculator, no water savings will exist, and therefore that columns will not exist. Since the field does not exist, the append query throws a dialog box to ask me the parameter value. What I'm trying to accomplish is for it not to do this. I don't have a preference whether it just ignores the field, or if it defaults to 0. Any thoughts? Here are my queries that make the whole thing work:
[AppendFilter]
SELECT csv.*
FROM csv LEFT JOIN sharepoint ON (csv.FIM_Unique = sharepoint.FIM_Unique) AND (csv.[Building ID] = sharepoint.Building)
WHERE (((sharepoint.FIM_Unique) Is Null) AND ((sharepoint.Building) Is Null) AND ((csv.FIM_Unique) Is Not Null));
[AppendData]
INSERT INTO sharepoint ( FIM_Unique, FIM_Designation, [FIM Description], Safety_Factor, Building, [kWh Savings], [kW Savings], [Natural Gas Savings], [Water Savings], [Fuel Oil Savings], [Propane Savings] )
SELECT [AppendFilter].FIM_Unique, csv.[FIM Designation], csv.[FIM Description], csv.[Safety Factor], [AppendFilter].[Building ID], [AppendFilter].[kWh Savings], [AppendFilter].[kW Savings], [AppendFilter].[Natural Gas Savings], [AppendFilter].[Water Savings], [AppendFilter].[Fuel Oil Savings], [AppendFilter].[Propane Savings]
FROM [AppendFilter]. INNER JOIN csv ON [AppendFilter].FIM_Unique = csv.FIM_Unique
WHERE ((([AppendFilter].FIM_Unique) Is Not Null) AND (([AppendFilter].[Building ID]) Is Not Null));
I have tried DoCmd.SetWarnings False when running the query, but these warnings appear immune to that!
Could use VBA to dynamically build and execute SQL action. One way is with DAO TableDefs to iterate through table fields and build a string of field names and use string variable to build SQL statement. I don't think need to join CSV with AppendFilter. For one thing, doing so results in a SELECT query with duplicate field names and that will cause confusion unless fields are prefixed with table name.
However, gets complicated if field names are not same in source and destination tables. I see some variation with underscores and Building ID as opposed to Building. Since there are only 3 fields with variation, build string variable with conditional code to check for particular field names and make adjustment as appropriate.
Dim db As DAO.Database, td As DAO.TableDef, fd As DAO.Field, strD As String, strF As String
Set db = CurrentDb
Set td = db.TableDefs("csv")
For Each fd In td.Fields
strF = fd.Name
If strF = "FIM Designation" Or strF = "Safety Factor" Then strF = Replace(strF, " ", "_")
If strF = "Building ID" Then strF = "Building"
strD = strD & "[" & strF & "],"
Next
strD = Left(strD, Len(strD) - 1)
db.Execute "INSERT INTO sharepoint(" & strD & ") " & _
"SELECT * FROM csv " & _
"WHERE FIM_Unique IN(SELECT FIM_Unique FROM AppendFilter)"
I strongly advise not to use spaces nor punctuation/special characters in naming convention. Underscore is only exception but I never use that either.

Junction between categories

I am making a database for a freelance sign language interpreter. I have a subject table tblClient which holds information regarding the freelancer's clients. There is a lookup table tlkClientClientType showing the various types of clients (categorized on condition -- deaf, deaf/blind, etc). There is also a table called tlkClientModality. In this table are the different types of sign language offered by the interpreter. You can pick the client's preferred modality from the Client table.
Now the tricky part. There are certain modalities (basically just flavors of sign language) that should not be available to pick if the client is a certain type. For example, you cannot use "regular" (visual) sign language with someone who is deaf/blind. This is because regular sign language depends on the person being able to see. Instead, one would use "Tactile ASL" which is a hand-over-hand version of sign language. Basically, I want to limit the modality list based on the client type picked.
To start off my associations, I am making a junction table between the tlkClientClientType and tlkClientModality tables. Here, I will create the correct allowable pairs of client types and modalities. In addition, there is not really a necessary "junction field" to justify this relationship. I am considering making a "dummy field" as a way to still justify such a relationship.
Later, in a form for the client, I will edit the row source query on the modality combo box to be dependent on the choice selected in the client type box. This would be accomplished by checking what records in the junction table match the choice in the client type combo box.
Am I on the right track here? Making a junction table between two lookup tables seems weird. Is there anything wrong with it?
Note -- I would like to stay away from VBA, but I am up to writing macros to accomplish these goals. Any thoughts would be appreciated.
A photo of the relationships is given below.
Relationshipsphoto
Keep your tables in whatever normal form works for your business case. Your choice just changes how the information is stored which means you have to change how you write your queries as the information may be located in a different place. I chose a many to many relationship between client and clienttype.
You want to set the modality combobox contents from the client combobox. This is a problem of the view not the model hence we change the view and not the organization of the data. the key is to set the recordsource of the modality combobox from the afterupdate event of the client combobox.
Showing the results first: if the client is deaf we get:
if the client is blind or blind and deaf we get:
Private Sub cmbClient_AfterUpdate()
Dim RegularASL As Integer: RegularASL = 1
Dim TactileASL As Integer: TactileASL = 2
Dim ClientID As Integer: ClientID = Me.cmbClient
If ClientisBlindandDeaf(ClientID) Then
cmbModality.RowSource = "SELECT * FROM tlkClientModality WHERE ClientModalityID = " & TactileASL
ElseIf isClientBlind(ClientID) Then
cmbModality.RowSource = "SELECT * FROM tlkClientModality WHERE ClientModalityID = " & TactileASL
Else
cmbModality.RowSource = "SELECT * FROM tlkClientModality WHERE ClientModalityID = " & RegularASL
End If
cmbModality.Requery 'reload cmbmodality data
Me.Refresh 'repaint cmbmodality
End Sub
Public Function isClientBlind(ClientID As Integer) As Boolean
'Get rid of * in names vba can't parse the *
Dim isblind: isblind = 2
Dim ClientClientTypeID As Variant 'allows null return type
ClientClientTypeID = DLookup("ClientClientTypeID", "tlkClientClientType", "ClientID = " & ClientID & " AND ClientTypeID = " & isblind)
If IsNull(ClientClientTypeID) Then
isClientBlind = False
Else
isClientBlind = True
End If
End Function
Public Function isClientDeaf(ClientID As Integer) As Boolean
Dim isdeaf: isdeaf = 1
Dim ClientClientTypeID As Variant 'allows null return type
ClientClientTypeID = DLookup("ClientClientTypeID", "tlkClientClientType", "ClientID = " & ClientID & " AND ClientTypeID = " & isdeaf)
If IsNull(ClientClientTypeID) Then
isClientDeaf = False
Else
isClientDeaf = True
End If
End Function
Public Function ClientisBlindandDeaf(ClientID As Integer) As Boolean
If isClientBlind(ClientID) And isClientDeaf(ClientID) Then
ClientisBlindandDeaf = True
Else
ClientisBlindandDeaf = False
End If
End Function
note: cmbModality is set up just like it is bound to the entire tlkModality table then the record source changes are used to filter it in effect.

Access will only retrieve data from 1 table in a join with no where clause

I am using MS-Access to get information from 2 tables. I have used inner join, left, right, and outer with all variations, and it will either pull 1 row when 316 are expected, all data for the fields in test with no values for the fields from test 1, or all data for fields from test 1, and no values for test. How do I resolve this? The actual fields had to be changed for privacy, but the below is the exact layout.
SELECT [TEST].a,
[TEST].b,
[TEST].c,
[TEST 1].[D],
[TEST].E,
[TEST].F,
[TEST].G,
[TEST].H,
[TEST 1].[I],
[TEST].J,
[TEST].K,
[TEST 1].L,
[TEST 1].[M]
FROM [TEST 1]
INNER JOIN [TEST] ON [TEST 1].[ID] = [TEST].[CLAIMSNO];
This is a data-validation and debugging exercise, so if you can't share concrete example data then there is really no definite answer to this question. Technically it may not be answerable according to common StackOverflow standards, but I feel generous right now.
Since the joined fields are text, there are various possibilities keeping them from matching: extra spaces, null-terminated strings, case sensitivity (although Access by default should be case insensitive), wide (Unicode) vs narrow (ASCII, UTF-8) encoding, etc. You did not reveal where the data came from, nor how it was loaded into the database, so I make no assumptions. In order to understand the data and determine the reason for the failed matches, you need to investigate the details of the strings. You could likely narrow the problem by investigating the source of the data values and understanding the range of possible characters, encoding, string termination, etc.
Since you are already having troubles matching data AND since you already indicated that the original tables had no primary key or indexes, I highly recommend adding a new AutoNumber field with a unique index to each table, perhaps named [AID] (for AutoNumber ID). Do this even if you have added indices to existing columns. This will at least provide a reliable "handle" to select and refer to a particular row while debugging the other columns.
The big idea is to use VBA or other built-in functions to inspect and report on various attributes of the string values. There are just too many ways you could do this, but my preference is to create a public VBA function in a normal VBA module and then call this function from an SQL query. Although you could do this for every row, instead I suggest manually choosing rows from each table which you think should match... record the [AID] value for each row. If the manually-selected rows don't result in anything enlightening, then run it against an entire table and see what interesting results you get.
Consider these functions:
Public Function CheckSpaces(val As Variant) As String
Dim result As String
If IsNull(val) Then
result = "Null"
ElseIf VarType(val) = VbVarType.vbString Then
If Len(val) = 0 Then
result = "Empty String"
Else
Dim temp As String
Dim n As Integer, m As Integer
n = Len(val)
result = "Length " & n
temp = LTrim(val)
m = Len(temp)
If n <> m Then
result = result & " AND " & (n - m) & " left spaces"
End If
temp = RTrim(val)
m = Len(temp)
If n <> m Then
result = result & " AND " & (n - m) & " right spaces"
End If
End If
Else
result = "Not a string!"
End If
CheckSpaces = result
End Function
Public Function NullChar(val As Variant) As Boolean
Dim result As Boolean
result = False
If Not IsNull(val) Then
If VarType(val) = VbVarType.vbString Then
If InStr(val, vbNullChar) > 0 Then 'vbNullChar = Chr(0)
result = True
End If
End If
End If
NullChar = result
End Function
And execute queries similar to the following. Let's say that [Test 1] row AID = 10 has [ID] == 'name'. Likewise, imagine row AID == 20 of [Test] has [CLAIMSNO] = ' name ':
SELECT [ID], CheckSpaces([ID]), NullChar([ID])
FROM [TEST 1]
WHERE [AID] = 10
and
SELECT [CLAIMSNO], CheckSpaces([CLAIMSNO]), NullChar([CLAIMSNO])
FROM [TEST]
WHERE [AID] = 20
Compare the returned values. Is there anything that indicates a failed match?

Deleting duplicate data in MySQL

I'm trying to emulate the accepted answer in this SO question: Delete all Duplicate Rows except for One in MySQL? [duplicate] with a twist, I want the data (auto-incrementing ID's) of one table to determine which rows to delete in another table. SQLFiddle here showing data.
In the fiddle referenced above, the end result I'm looking for is the rows in eventdetails_new with Event_ID = 4 & 6 to be deleted (EVENTDETAILS_ID's 5 & 6, and 9 & 10), leaving rows 3 & 5 (EVENTDETAILS_ID's 3 & 4 and 7 & 8). I hope that made sense. Ideally the rows in events_new with those same Event_ID's would get deleted as well (which I haven't started working on yet, so no code samples).
This is the query I'm trying to make work, but I'm a bit over my head:
SELECT *
FROM eventdetails_new AS EDN1, eventdetails_new AS EDN2
INNER JOIN events_new AS E1 ON `E1`.`Event_ID` = `EDN1`.`Event_ID`
INNER JOIN events_new AS E2 ON `E2`.`Event_ID` = `EDN2`.`Event_ID`
WHERE `E1`.`Event_ID` > `E2`.`Event_ID`
AND `E1`.`DateTime` = `E2`.`DateTime`
AND events_new.EventType_ID = 6;
Here's the same SQLFiddle with the results of this query. Not good. I can see the Event_ID in the data, but the query cannot for some reason. Not sure how to proceed to fix this.
I know it's a SELECT query, but I couldn't figure out a way to have two aliased tables in the DELETE query (which I think I need?). I figured if I could get a selection, I could delete it with some C# code. However ideally it could all be done in a single query or set of statements without having to go outside of MySQL.
Here's my first cut at the query, but it's just as bad:
DELETE e1 FROM eventdetails_new e1
WHERE `events_new`.`Event_ID` > `events_new`.`Event_ID`
AND events_new.DateTime = events_new.DateTime AND events_new.EventType_ID = 6;
SQLFiddle won't let me run this query at all, so it's not much help. However, it give me the same error as the one above: Error Code: 1054. Unknown column 'events_new.Event_ID' in 'where clause'
I'm by no means married to either of these queries if there's a better way. The end result I'm looking for is deleting a bunch of duplicate data.
I have hundreds of thousands of these results, and I know that roughly 1/3 of them are duplicates that I need to get rid of before we go live with the database.
Here's what I eventually ended up doing. My co-worker & I came up with a query that would give us a list of Event_ID's that had duplicate data (we actually used Access 2010's query builder and MySQL-ified it). Bear in mind this is a complete solution where the original question didn't have as much detail as far as linked tables. If you've got questions about this, feel free to ask & I'll try to help:
SELECT `Events_new`.`Event_ID`
FROM Events_new
GROUP BY `Events_new`.`PCBID`, `Events_new`.`EventType_ID`, `Events_new`.`DateTime`, `Events_new`.`User`
HAVING (((COUNT(`Events_new`.`PCBID`)) > 1) AND ((COUNT(`Events_new`.`User`)) > 1) AND ((COUNT(`Events_new`.`DateTime`)) > 1))
From this I processed each Event_ID to remove the duplicates in an iterative manner. Basically I had to delete all the child rows starting from the last lowest table so that I didn't run afoul of foreign key restraints.
This chunk of code was written in LinqPAD as C# statements: (sbCommonFunctions is an inhouse DLL designed to make most (but not all as you'll see) database functions be handled the same way or easier)
sbCommonFunctions.Database testDB = new sbCommonFunctions.Database();
testDB.Connect("production", "database", "user", "password");
List<string> listEventIDs = new List<string>();
List<string> listEventDetailIDs = new List<string>();
List<string> listTestInformationIDs = new List<string>();
List<string> listTestStepIDs = new List<string>();
List<string> listMeasurementIDs = new List<string>();
string dtQuery = (String.Format(#"SELECT `Events_new`.`Event_ID`
FROM Events_new
GROUP BY `Events_new`.`PCBID`,
`Events_new`.`EventType_ID`,
`Events_new`.`DateTime`,
`Events_new`.`User`
HAVING (((COUNT(`Events_new`.`PCBID`)) > 1)
AND ((COUNT(`Events_new`.`User`)) > 1)
AND ((COUNT(`Events_new`.`DateTime`)) > 1))"));
int iterations = 0;
DataTable dtEventIDs = getDT(dtQuery, testDB);
while (dtEventIDs.Rows.Count > 0)
{
Console.WriteLine(dtEventIDs.Rows.Count);
Console.WriteLine(iterations);
iterations++;
foreach(DataRowView eventID in dtEventIDs.DefaultView)
{
listEventIDs.Add(eventID.Row[0].ToString());
DataTable dtEventDetails = testDB.QueryDatabase(String.Format(
"SELECT * FROM EventDetails_new WHERE Event_ID = {0}",
eventID.Row[0]));
foreach(DataRowView drvEventDetail in dtEventDetails.DefaultView)
{
listEventDetailIDs.Add(drvEventDetail.Row[0].ToString());
}
DataTable dtTestInformation = testDB.QueryDatabase(String.Format(
#"SELECT TestInformation_ID
FROM TestInformation_new
WHERE Event_ID = {0}",
eventID.Row[0]));
foreach(DataRowView drvTest in dtTestInformation.DefaultView)
{
listTestInformationIDs.Add(drvTest.Row[0].ToString());
DataTable dtTestSteps = testDB.QueryDatabase(String.Format(
#"SELECT TestSteps_ID
FROM TestSteps_new
WHERE TestInformation_TestInformation_ID = {0}",
drvTest.Row[0]));
foreach(DataRowView drvTestStep in dtTestSteps.DefaultView)
{
listTestStepIDs.Add(drvTestStep.Row[0].ToString());
DataTable dtMeasurements = testDB.QueryDatabase(String.Format(
#"SELECT Measurements_ID
FROM Measurements_new
WHERE TestSteps_TestSteps_ID = {0}",
drvTestStep.Row[0]));
foreach(DataRowView drvMeasurements in dtMeasurements.DefaultView)
{
listMeasurementIDs.Add(drvMeasurements.Row[0].ToString());
}
}
}
}
testDB.Disconnect();
string mysqlConnection =
"server=server;\ndatabase=database;\npassword=password;\nUser ID=user;";
MySqlConnection connection = new MySqlConnection(mysqlConnection);
connection.Open();
//start unwinding the duplicates from the lowest level upward
whackDuplicates(listMeasurementIDs, "measurements_new", "Measurements_ID", connection);
whackDuplicates(listTestStepIDs, "teststeps_new", "TestSteps_ID", connection);
whackDuplicates(listTestInformationIDs, "testinformation_new", "testInformation_ID", connection);
whackDuplicates(listEventDetailIDs, "eventdetails_new", "eventdetails_ID", connection);
whackDuplicates(listEventIDs, "events_new", "event_ID", connection);
connection.Close();
//update iterator from inside the clause in case there are more duplicates.
dtEventIDs = getDT(dtQuery, testDB); }
}//goofy curly brace to allow LinqPAD to deal with inline classes
public void whackDuplicates(List<string> listOfIDs,
string table,
string pkID,
MySqlConnection connection)
{
foreach(string ID in listOfIDs)
{
MySqlCommand command = connection.CreateCommand();
command.CommandText = String.Format(
"DELETE FROM " + table + " WHERE " + pkID + " = {0}", ID);
command.ExecuteNonQuery();
}
}
public DataTable getDT(string query, sbCommonFunctions.Database db)
{
return db.QueryDatabase(query);
//}/*this is deliberate, LinqPAD has a weird way of dealing with inline
classes and the last one can't have a closing curly brace (and the
first one has to have an extra opening curly brace above it, go figure)
*/
Basically this is a giant while loop, and the clause iterator is updated from inside the clause until the number of Event_ID's drops to zero (it takes 5 iterations, some of the data has as many as six duplicates).

How to write Case Sensitive Query for MS Access?

I want to know the Select Query for MS Access with case sensitive.
I have two values for VitualMonitorName as below
VCode VirtualMonitorName
Row 1 (1, 'VM1');
Row 2 (2, 'Vm1');
Here both values are different.
If I write
"SELECT VCode FROM VirtualMaster WHERE VirtualMonitorName like '" + Vm1 + "'";
It replies VCode = 1 Only.
You can use the StrComp() function with vbBinaryCompare for a case-sensitive comparison. Here is an example from the Immediate window to show how StrComp() works. See the Access help topic for more details.
? StrComp("a", "A", vbBinaryCompare)
1
? StrComp("a", "A",vbTextCompare)
0
StrComp() returns 0 if the first two arguments evaluate as equal, 1 or -1 if they are unequal, and Null if either argument is Null.
To use the function in a query, supply the vbBinaryCompare constant's value (0) rather than its name.
SELECT VCode
FROM VirtualMaster
WHERE StrComp(VirtualMonitorName, "Vm1", 0) = 0;
This approach is also available to queries from other applications if they use the newer Access Database Engine ("ACE") drivers. For example, the following C# code
string myConnectionString =
#"Driver={Microsoft Access Driver (*.mdb, *.accdb)};" +
#"Dbq=C:\Users\Public\Database1.accdb;";
using (OdbcConnection con = new OdbcConnection(myConnectionString))
{
con.Open();
using (var cmd = new OdbcCommand())
{
cmd.Connection = con;
cmd.CommandText =
"SELECT COUNT(*) AS n FROM [VirtualMaster] " +
"WHERE StrComp([VirtualMonitorName],?,?) = 0";
cmd.Parameters.AddWithValue("?", "Vm1");
cmd.Parameters.Add("?", OdbcType.Int);
var vbCompareOptions = new Dictionary<string, int>()
{
{"vbBinaryCompare", 0},
{"vbTextCompare", 1}
};
string currentOption = "";
currentOption = "vbBinaryCompare";
cmd.Parameters[1].Value = vbCompareOptions[currentOption];
Console.WriteLine(
"{0} found {1} record(s)",
currentOption,
Convert.ToInt32(cmd.ExecuteScalar()));
currentOption = "vbTextCompare";
cmd.Parameters[1].Value = vbCompareOptions[currentOption];
Console.WriteLine(
"{0} found {1} record(s)",
currentOption,
Convert.ToInt32(cmd.ExecuteScalar()));
}
}
produces
vbBinaryCompare found 1 record(s)
vbTextCompare found 2 record(s)
Check this out:
https://support.microsoft.com/kb/244693?wa=wsignin1.0
This article describes four methods of achieving a case-sensitive JOIN using the Microsoft Jet database engine. Each of these methods has advantages and disadvantages that should be weighed before choosing an implementation. The methods are:
StrComp
Case-Sensitive IISAM Driver
Hexadecimal Expansion
Binary Field
Using only built-in functions, add an additional custom column in the query design view:
location: InStr(1,[VCode],"VM1",0)
the zero parameter requests binary compare (case sensitive) when finding location of "VM1" within [VCode]
set the criteria in that column to >0 so only records with non-zero location in the vcode matching Like "*vm*" contain the exact VM1 string -
The WHERE clause looks like:
WHERE (((VirtualMaster.VCode) Like "\*vm*") AND ((InStr(1,[VCode],"VM1",0))>0));
Using at a simpler level of coding.
As a condition in a DCOUNT operation, checking on a Field (Column) that has to have the correct Case, and ignoring Blank States/Territories.
' lngcounter will count the all States
' or Territories Field ( Column) with this
' exact case value of 'Ohio'. ([ID] is an Autonumber ID field)
lngCounter = DCount("[id]", Trim(Me!tboDwellingTablename), "[State/territory],'Ohio',0) = 0")
This only does one letter:
MS-ACCESS SQL:
SELECT Asc(Left([Title],1)) AS t FROM Master WHERE (((Asc(Left([Title],1)))=105));
Title is the field you want to search
Master is the Table where Title field is located
105 Ascii code for character..
In this case only Title's that start with i not I
If you want to search for lower case "a" you would change the 105 to 97