I agree that loops in SQL are not great. Loops in programming languages are fine obviously. There are just many more and nicer constructs in a programming language to manipulate data.
Maybe you have a root table that is anchoring your query, say a Staff table. The Staff table has a Manager column, which is another row in the Staff table. You then need to do a bunch of aggregate stuff. So in a programming language, you can maybe query the database 3 times, once for the staff you need, and then again for maybe shifts completed and so on.
You can then easily put the staff into a dictionary with virtually no code. Then you loop over the non-dictionary staff array, and you have something like:
for (var staff in shittyStaff) {
processedStaff.add({
manager: staffDic[staff.manager],
shiftsCompleted: shifts[staff.id].length,
shiftsWithManager: shifts[staff.id].filter(s => s.coworker == staff.manager.id).length
});
Or if it is setup with entity framework/c# stuff, it is just: var staff = db.Staff
.Include(s => s.Manager)
.Include(s => s.Shifts)
.Select(s => new {
Manager: s.Manager,
ShiftsCompleted: s.Shifts.Count(),
ShiftsWithManager: s.Shifts.Where(shift => shift.CoWorker == s.Manager).Count()
});
Probably a terrible and not particularly complex example because I made it up, but to do that in SQL requires a lot more stuff, a self-join, group by with count etc, etc.It depends a little on the exact columns you need, but it would look something like this, which is a supper common form for a SQL query:
select
m.id Manager,
count(sh.id) as ShiftsCompleted,
sum(iif(sh.coworker = m.id,1,0) as ShiftsWithManager
from staff s
left join manager m
on m.id = s.manager_id
left join shifts sh
on sh.id = s.shifts_id
group by s.id, m.id
The SQL version has the advantage that it's more intuitive to specify the columns that you need so if your query is running slow because you're pulling too much data (something that's happened to me a bunch,) you can omit unneeded columns pretty easily.
I guess this is where we differ. I've written many SQL queries many hundreds of lines long taking advantage of all kinds of SQL features. I don't see how I could make them "nicer" by writing them in Javascript: SQL has plenty of warts, but well-formatted and organized SQL is hard to beat for expressing exactly what you want without all the cruft associated with how you;re getting it. Once you know what you want, it comes out pretty quick (IME), only your typing speed is the limit. I find you have to think much more carefully about what you're doing in other languages because you have to think more about how to do it without the db abstracting all of that away.
IME loops in SQL are a huge code smell - everything should almost always be done using set logic to be clean and performant.