Does Sqlite support complex joins?

I’m using sqlite to write unit tests against repository code. I’m running into a more complex query not working with sqlite that works in sql server. It’s possible I have an issue in my seed data but I’m struggling to narrow down what’s not working.

Should I expect this query to work:

var q = db.From<MerchantEntity>()
   .Join<MerchantContact>((m, mc) => m.Id == mc.MerchantId && mc.ContactTypeId == 1, 
      db.JoinAlias("mcd"))
   .Join<MerchantContact, AddressEntity>((mc, a) => Sql.JoinAlias(mc.AddressId, "mcd") == a.ID, 
      db.JoinAlias("ad"))
   .Join<AddressEntity, State>((a, s) => Sql.JoinAlias(a.StateId, "ad") == s.Id, 
      db.JoinAlias("sd"))
   .Join<MerchantContact>((m, mc) => m.Id == mc.MerchantId && mc.ContactTypeId == 2, 
      db.JoinAlias("mcl"))
   .Join<MerchantContact, AddressEntity>((mc, a) => Sql.JoinAlias(mc.AddressId, "mcl") == a.ID, 
      db.JoinAlias("al"))
   .Join<AddressEntity, State>((a, s) => Sql.JoinAlias(a.StateId, "al") == s.Id, 
      db.JoinAlias("sl"))
   .Where<AddressEntity>(a => 
          Sql.JoinAlias(a.AddressLine1, "ad") == dba.AddressLine1.Trim()
            && Sql.JoinAlias(a.City, "ad") == dba.City.Trim()
            && Sql.JoinAlias(a.Zip, "ad") == dba.Zip.Trim()
            && Sql.JoinAlias(a.AddressLine1, "al") == legal.AddressLine1.Trim()
            && Sql.JoinAlias(a.City, "al") == legal.City.Trim()
            && Sql.JoinAlias(a.Zip, "al") == legal.Zip.Trim())
   .And<State>(s => Sql.JoinAlias(s.StateShortName, "sd") == dba.State.Trim()
                    && Sql.JoinAlias(s.StateShortName, "sl") == legal.State.Trim())
   .Select(Sql.Count("*"));
var merchCount = db.Scalar<int>(q); 
   

When configuring my Set-up as follows:

        [OneTimeSetUp]
        public void TestFixtureSetUp()
        {
            this._appHost = new BasicAppHost().Init();
            var container = this._appHost.Container;

            container.Register<IDbConnectionFactory>(new OrmLiteConnectionFactory(":memory:", SqliteDialect.Provider));

            using (var db = container.Resolve<IDbConnectionFactory>().Open())
            {
                db.DropAndCreateTable<State>();
                db.DropAndCreateTable<AddressEntity>();
                db.DropAndCreateTable<MerchantContact>();
                db.DropAndCreateTable<MerchantEntity>();

                db.InsertAll(BoardingTests.Data.SeedData.StatesSeedList);
                db.InsertAll(BoardingTests.Data.SeedData.AddressSeedList);
                db.InsertAll(BoardingTests.Data.SeedData.MerchantSeedList);
                db.InsertAll(BoardingTests.Data.SeedData.MerchantContactSeedList);
            }
        }

Thanks

Can you create a stand-alone example of what doesn’t work in Sqlite in Gistlyn please:

http://gistlyn.com/?gist=0cd558e817f28f77b974c44c3e12ff6f&collection=991db51e44674ad01d3d318b24cf0934

So I just tried this and simple cases seem to work in Gistlyn but not in my project. I’ll try my exact code in Gistlyn at a later time - I’ll follow up if I can’t get it working.

1 Like

Hi!

I’m back from vacation and reproduced the query/code that I think should work in sqllite in memory (it works against MSSQL outside of unit tests).

I think this is the link to it but let me know if it doesn’t work - http://gistlyn.com/?gist=0cd558e817f28f77b974c44c3e12ff6f&collection=2cc6b5db6afd3ccb0d0149e55fdb3a6a

Thanks

Liz

That link is to the default OrmLite example, it looks like you forgot to save or copied the wrong url.

Attempt 2 - http://gistlyn.com/?gist=92713733225443fc33a5d74f718901e2&collection=2cc6b5db6afd3ccb0d0149e55fdb3a6a

Let me know - thanks

I’ve ran this example on SQL Server and they are both reporting the same results, i.e:

(Seemingly) Working test - should be false: False
Failing test - should be true: False

SQL Server

You can enable a debug logger to loge the generated SQL, e.g. to your console with:

LogManager.LogFactory = new ConsoleLogFactory();
DEBUG: SQL: SELECT COUNT(*) 
FROM "Merchant" INNER JOIN "MerchantContact" mcd ON (("Merchant"."Id" = "mcd"."MerchantId") AND ("mcd"."ContactTypeId" = 1)) INNER JOIN "Address" ad ON (mcd."AddressId" = "ad"."ID") INNER JOIN "State" sd ON (ad."StateId" = "sd"."Id") INNER JOIN "MerchantContact" mcl ON (("Merchant"."Id" = "mcl"."MerchantId") AND ("mcl"."ContactTypeId" = 2)) INNER JOIN "Address" al ON (mcl."AddressId" = "al"."ID") INNER JOIN "State" sl ON (al."StateId" = "sl"."Id")
WHERE ((((((ad."AddressLine1" = @0) AND (ad."City" = @1)) AND (ad."Zip" = @2)) AND (al."AddressLine1" = @3)) AND (al."City" = @4)) AND (al."Zip" = @5)) AND ((sd."StateShortName" = @6) AND (sl."StateShortName" = @7))
PARAMS: @0=1538 Western Ave, @1=Chicago, @2=60660, @3=325 MOUNTAIN WAY DR, @4=OREM, @5=84058, @6=IL, @7=UT

(Seemingly) Working test - should be false: False

DEBUG: SQL: SELECT COUNT(*) 
FROM "Merchant" INNER JOIN "MerchantContact" mcd ON (("Merchant"."Id" = "mcd"."MerchantId") AND ("mcd"."ContactTypeId" = 1)) INNER JOIN "Address" ad ON (mcd."AddressId" = "ad"."ID") INNER JOIN "State" sd ON (ad."StateId" = "sd"."Id") INNER JOIN "MerchantContact" mcl ON (("Merchant"."Id" = "mcl"."MerchantId") AND ("mcl"."ContactTypeId" = 2)) INNER JOIN "Address" al ON (mcl."AddressId" = "al"."ID") INNER JOIN "State" sl ON (al."StateId" = "sl"."Id")
WHERE ((((((ad."AddressLine1" = @0) AND (ad."City" = @1)) AND (ad."Zip" = @2)) AND (al."AddressLine1" = @3)) AND (al."City" = @4)) AND (al."Zip" = @5)) AND ((sd."StateShortName" = @6) AND (sl."StateShortName" = @7))
PARAMS: @0=1412 RUSSELL PKWY, @1=WARNER ROBINS, @2=31088, @3=1412 RUSSELL PKWY, @4=WARNER ROBINS, @5=31088, @6=GA, @7=GA

Failing test - should be true: False

SQLite

DEBUG: SQL: SELECT COUNT(*) 
FROM "Merchant" INNER JOIN "MerchantContact" mcd ON (("Merchant"."Id" = "mcd"."MerchantId") AND ("mcd"."ContactTypeId" = 1)) INNER JOIN "Address" ad ON (mcd."AddressId" = "ad"."ID") INNER JOIN "State" sd ON (ad."StateId" = "sd"."Id") INNER JOIN "MerchantContact" mcl ON (("Merchant"."Id" = "mcl"."MerchantId") AND ("mcl"."ContactTypeId" = 2)) INNER JOIN "Address" al ON (mcl."AddressId" = "al"."ID") INNER JOIN "State" sl ON (al."StateId" = "sl"."Id")
WHERE ((((((ad."AddressLine1" = @0) AND (ad."City" = @1)) AND (ad."Zip" = @2)) AND (al."AddressLine1" = @3)) AND (al."City" = @4)) AND (al."Zip" = @5)) AND ((sd."StateShortName" = @6) AND (sl."StateShortName" = @7))
PARAMS: @0=1538 Western Ave, @1=Chicago, @2=60660, @3=325 MOUNTAIN WAY DR, @4=OREM, @5=84058, @6=IL, @7=UT

(Seemingly) Working test - should be false: False

DEBUG: SQL: SELECT COUNT(*) 
FROM "Merchant" INNER JOIN "MerchantContact" mcd ON (("Merchant"."Id" = "mcd"."MerchantId") AND ("mcd"."ContactTypeId" = 1)) INNER JOIN "Address" ad ON (mcd."AddressId" = "ad"."ID") INNER JOIN "State" sd ON (ad."StateId" = "sd"."Id") INNER JOIN "MerchantContact" mcl ON (("Merchant"."Id" = "mcl"."MerchantId") AND ("mcl"."ContactTypeId" = 2)) INNER JOIN "Address" al ON (mcl."AddressId" = "al"."ID") INNER JOIN "State" sl ON (al."StateId" = "sl"."Id")
WHERE ((((((ad."AddressLine1" = @0) AND (ad."City" = @1)) AND (ad."Zip" = @2)) AND (al."AddressLine1" = @3)) AND (al."City" = @4)) AND (al."Zip" = @5)) AND ((sd."StateShortName" = @6) AND (sl."StateShortName" = @7))
PARAMS: @0=1412 RUSSELL PKWY, @1=WARNER ROBINS, @2=31088, @3=1412 RUSSELL PKWY, @4=WARNER ROBINS, @5=31088, @6=GA, @7=GA

Failing test - should be true: False

How did you test this against sql server?

Running the raw query against my production database returns a count of 1 - I’ve pretty much copied the seed data from those tables. Running the code I provided you I get 0.

I ran the code in an NUnit test and switched the provider from SQLite to SQL Server and re ran it. Are you testing this (I,e creating new tables with this test data) against an empty SQL Server database?

No - we use an existing database - would I be missing model related information - I don’t normally write ‘code-first’ entities that are actually used to produce a table

EDIT - I figured out the problem - I need to be able to turn off identity insert when inserting the seed data - I need full control over the primary key values when setting up the data - how do I do this?

i.e. without removing the Autoincrement attribute, I need the insert to allow me to override the primary key - is this possible without writing a custom insert?

EDIT 2 - found this from a previous answer -http://gistlyn.com/?gist=aa242a2bf9c6603ec391a86d9772e5eb - and flipping the flags at runtime works!

1 Like

You can tell OrmLite to not treat it as an AutoIncrementing Id with:

typeof(Table).GetModelMetadata().PrimaryKey.AutoIncrement = false;

But that only changes OrmLite’s behaviour, If needed you would still need to disable it in SQL Server with:

db.ExecuteSql($$"SET IDENTITY_INSERT {tableName} OFF");

//... 

db.ExecuteSql($$"SET IDENTITY_INSERT {tableName} ON");
1 Like