0

Please can you advise how I can write the following using NHibernate's QueryOver<>() Query<>() or other NHibernate method that will not involve me rewriting this (and numerous other similar queries) when switching DB provider?

The subqueries with multiple table joins are making this a pain for me to translate with my limited exposure of NHibernate.

return Session.CreateSQLQuery("select " +
                               "    boards.id, boards.name, boards.description, " +
                               "    (" +
                               "        select " +
                               "            count(topic.id) " +
                               "        from topic " +
                               "        left join users on users.id=topic.user " +
                               "        left join boards b on b.id=topic.bid " +
                               "        left join boards b2 on b2.id=b.bid " +
                               "        where (topic.bid=boards.id or b.bid = boards.id or b2.bid = boards.id) " +
                               "        and (b.type <= (:userType)) " +
                               "        and (b2.type is null or b2.type <= (:userType)) " +
                               "        and users.type > 0 " +
                               "        and users.chatban = 0" +
                               "    ) as TopicCount," +
                               "    (" +
                               "        select (max(posts.time) - max(read.time)) as t " +
                               "        from posts " +
                               "        left join users u on u.id=posts.user" +
                               "        left join topic on topic.id=posts.tid " +
                               "        left join read on read.topic=topic.id and read.userid=(:userId) " +
                               "        left join boards b on b.id=topic.bid" +
                               "        left join boards b2 on b2.id=b.bid" +
                               "        where " +
                               "            (topic.bid=boards.id or b.bid = boards.id or b2.bid = boards.id) " +
                               "            and (b.type <= (:userType)) " +
                               "            and (b2.type is null or b2.type <= (:userType)) " +
                               "            and not exists (select boardid from boardhigh where boardid=b.id and userid=(:userId))" +
                               "            and u.type > 0 " +
                               "            and u.chatban = 0 " +
                               "        group by topic.id " +
                               "        order by t desc " +
                               "        limit 1" +
                               "    ) as time " +
                               "from boards " +
                               "left join topic on topic.bid=boards.id " +
                               "where" +
                               "    boards.type <= (:userType) " +
                               (parentBoard.HasValue ? " and boards.bid = " + parentBoard.Value : "") +
                               "group by boards.id, boards.name, boards.description, boards.display " +
                               "order by boards.display desc, boards.name asc"
            )
            .SetInt32("userType", (int)UserHelper.LoggedInUser.UserType)
            .SetInt64("userId", UserHelper.LoggedInUser.Id)
            .List()
            .Cast<object[]>()
            .ToList()
            .Select(x => new BoardValueObject
            {
                Id = (int)x[0],
                Name = x[1].ToString(),
                Description = x[2].ToString(),
                TopicCount = (long)x[3],
                Time = x[4] as int?
            })
            .ToList();

Thanks

Andrew Smith
  • 210
  • 1
  • 8
  • Honestly, I'd either make it a stored procedure which you can call with two parameters or extract that SQL to a separate view, which you can then cleanly map in NHibernate and query freely. – Patryk Ćwiek Jan 21 '15 at 19:36
  • I'm rewriting a very large PHP project in to C# with NHibernate, this is just the first of a lot of complex queries that will need moving over. I'd like to see how this one is rewritten so I have an understanding to do the others. – Andrew Smith Jan 21 '15 at 19:48
  • both functions and views would also (potentially) require rewriting if I moved to a different DB provider, which is what I am trying to avoid. – Andrew Smith Jan 21 '15 at 20:55
  • 1
    This is *almost* possible in QueryOver, but the second subquery in the `SELECT` clause contains a column that's grouped on but not selected. Unfortunately it's not possible to do this in QueryOver--you must `SELECT` columns you group on... – Andrew Whitaker Jan 22 '15 at 01:44
  • I could rewrite the 2nd subquery to be more complex and not require the group by, but this knowledge, plus the accepted answer, make me think this isn't worth while and I should just rewrite when/if needed (this query probably won't need rewriting, but there are others that will) – Andrew Smith Jan 22 '15 at 06:49

1 Answers1

0

I would heavily doubt that you'll ever recieve answer for your giant SQL Query - and its conversion. Meanwhile, I would like to point out some differences in approach/thinking when using NHibernate.

  • Use entities first. Define your model, the business domain model with Users, Topics, Boards.
  • Spend some time to understand the lazy loading. Lot of data could be recieved by simple query to get User(s), and then nagivating to his boards, topics... All data needed will be loaded in separted SQL statements, but without complex querying.
  • Do check the advantages fo batch-loading, which is a technique how to avoid 1+N SELECT issue. It is really working well and has surprisingly large effect on amount of issued SQL statements.
  • DO NOT WORRY to use native SQL to recieve "..." data required by users (replace dots with words like crazy, unbelievable, complex). NHibernate is ORM at first place. It does great job for all CRUD operations related to Business object model. Any "SP like selects" are supported but indirectly... via CreateSQLQuery

So, try to start with simple stuff and once that is working, even the complex one will.

Community
  • 1
  • 1
Radim Köhler
  • 122,561
  • 47
  • 239
  • 335
  • I'm currently doing a 2 stage rewrite of a website I have developed over the course of 10.5 years, stage 1 is just a code rewrite, with the database switchover (from postgres to sql server) happening when I have more time. I have already generated the entity classes, mapped most of the more crucial FK relations, and (I think) I configured batch-loading but I just couldn't get this query exported correctly. Thanks for your advice. – Andrew Smith Jan 22 '15 at 06:52