tags:

views:

101

answers:

1

Hey,

I am dealing with some queries for my assignment and any help would be greatly appreciated.

  1. List branches together with the number of employees and assigned to them customers, total loan amount, total account balance, assets for branches residing in the given city.
  2. List customers who made account and loan operations in the given time period
  3. List employees and the number of served by each of them customers in the given time period

I guess a simple example would be enough for me to solve rest.

Here's what I've tried so far for the first one:

ALTER PROCEDURE [dbo].[SelecBranchesByCity]
    (@City varchar(50))
AS
    select
        Br.Name as BranchName,
        COUNT(emps.ID) as NumberOfEmployee,
        SUM(emps.NumberOfCustomers) as TotalCustomers,
        SUM(lo.Amount) as TotalAmountOfLoan,
        SUM(acc.Balance) as TotalBalance,
        Br.Assets as Assets
    from Branches Br
    left outer join Employees emps on emps.[BranchName] = Br.Name
    left outer join Loans lo on lo.[BranchName] = Br.Name
    left outer join Accounts acc on acc.[BranchName] = Br.Name
    where
        Br.[Address] like '%'+@City+'%'
    GROUP BY
        Br.ID,
        Br.Name,
        Br.Assets

Here is the schema ! alt text

+2  A: 

Schema is frightening. Many-to-many on customers-loans? Many-to-many on customers-accounts? Why?? Employees has a branchname column instead of an FK relationship to branches. loan_operations has FK to employees!? I don't mean to stray from the topic or sound flippant, but there are so many anti-patterns here I don't even know where to begin. But I will try to help with the specific question anyway.

Q1:

CREATE PROCEDURE GetBranchSummaryByCity
    @City varchar(50)
AS
SELECT
    b.id, b.address, b.name, b.assets,
    b2.EmployeeCount, b2.CustomerCount,
    b2.TotalLoanAmount, b2.TotalAccountBalance
FROM branches b
INNER JOIN
(
    SELECT
        b.id,
        ISNULL(COUNT(DISTINCT e.id), 0) AS EmployeeCount,
        ISNULL(COUNT(c.id), 0) AS CustomerCount,
        ISNULL(SUM(l.amount), 0) AS TotalLoanAmount,
        ISNULL(SUM(a.balance), 0) AS TotalAccountBalance
    FROM branches b
    LEFT JOIN employees e
        -- Fix your schema so this matches the branch ID instead!
        ON e.branchname = b.name
    LEFT JOIN employee_customer ec
        ON ec.employeeid = e.id
    LEFT JOIN customers c
        ON c.id = ec.customerid
    LEFT JOIN customer_accounts ca
        ON ca.customerid = c.id
    LEFT JOIN accounts a
        ON a.id = ca.accountid
    LEFT JOIN loan_customer lc
        ON lc.customerid = c.id
    LEFT JOIN loans l
        ON l.id = lc.loanid
    WHERE b.Name LIKE '%' + @City + '%'
    GROUP BY b.id
) b2
ON b2.id = b.id

I'll note that you have an amount column in both loans and loan_operations. It's hard to know what the difference is between these two - it's entirely possible that loans shouldn't have this column at all, and instead it should be summed from the column in loan_operations.

Q2:

CREATE PROCEDURE FindCustomersWithLoansByDateRange
    @BeginDate datetime,
    @EndDate datetime
AS
SELECT c2.id, c2.name, c2.address, ...
FROM
(
    SELECT DISTINCT c.id
    FROM customers c
    INNER JOIN loan_customer lc
        ON lc.customerid = c.id
    INNER JOIN loans l
        ON l.id = lc.loanid
    INNER JOIN loan_operations lo
        ON lo.loanid = l.id
    WHERE lo.date BETWEEN @BeginDate AND @EndDate
) c1
INNER JOIN customers c2
ON c2.id = c1.id

Q3:

CREATE PROCEDURE GetEmployeeServiceSummaryByDateRange
    @BeginDate datetime,
    @EndDate datetime
AS
SELECT e.id, ISNULL(es.CustomerCount, 0) AS CustomerCount, e.name, e.position, ...
FROM employees e
LEFT JOIN
(
    SELECT e.id, COUNT(DISTINCT c.id) AS CustomerCount
    FROM employees e
    INNER JOIN employee_customer ec
        ON ec.employeeid = e.id
    INNER JOIN customers c
        ON c.id = ec.customerid
    INNER JOIN loan_customer lc
        ON lc.customerid = c.id
    INNER JOIN loans l
        ON l.id = lc.loanid
    INNER JOIN loan_operations lo
        ON lo.loanid = l.id
    WHERE lo.date BETWEEN @BeginDate AND @EndDate
    GROUP BY e.id
) es
ON es.id = e.id
Aaronaught
hey Aaron Thanks ! Yes the schema is so. But I am also programming an interface and I dont want to deal with FKs so much :) I want to do the min. reqs :) Thanks a million again !
Kubi
btw yes many to many for customers and loans as the assignment wants so :)
Kubi
ok I have some problems with the first ! first of all http://cmpe.emu.edu.tr/chefranov/Cmpe354-09Fall/Notes/CMPE%20354%20Fall%202009%20Term%20Project%20Task%20with%20grading%20policy.dochere is the assignment.and from the first query it always returns 0 for totalloanamount and totalaccountbalance ?
Kubi
What is your question? Why does it show 0 for the total? Probably because it **is** zero. What were you expecting instead What is your data?
Aaronaught
can it be because I added where b.[address] like '%'+@city+'%' Group By b.id, b.[address], b.name, b.assetsin the end of it ?
Kubi
"Many-to-many on customers-loans? Many-to-many on customers-accounts? Why??" -- not the answer for the OP, but I could see this in real life. Let's say me and a cosigner sign for a car loan and a personal loan, now we have a perfectly reasonable explanation for a many-to-many relationship between customers and loans. Let's also say I have checking account and a join savings account with my husband, so theres a perfectly reasonable explanation for a many-to-many relationship between customers and accounts.
Juliet
@Kubi: You are right, I forgot the group condition in #1, which I've now added. The zeros might have to do grouping by those extra columns, but I predict you'll still get the same results.
Aaronaught
@Juliet: I'll assert my tentative agreement with that, but suggest that in practice the "co-signer" relationship would be modeled explicitly (a loan belongs to one primary customer and may have *N* cosigners). `M:N` relationships always seem like a good idea at the start but become *so* painful to manage later, I usually try to avoid them if there's another way to model it.
Aaronaught
yes I am getting the same results. :(
Kubi
@Kubi: Just saw your comment on the other question and took a quick look at the assignment doc. I agree with Aaron and Juliet, your database schema needs a bit of work. The DB design counts for 15% of your mark, are you sure you want to risk it by doing the bare minimum? Personally, I hate DB work, but over the years I've come to appreciate the power and flexibility that a properly designed DB brings to an application. You're going to lose a lot of marks for things like leaving out foreign keys because you "dont want to deal with FKs so much". Anyway, thats my friendly warning.
Rory
@Aaron: Forgive me if I've missed something, it's been a very long day, but is there any reason you're using nested selects? I cannot for the life of me see a reason to use them in your answers for Q1 and Q2 since all info the outer select returns is available in the inner query. I get a similar feeling about Q3, but my mind was starting to bend by the time I reached that point. Something tells me its time for bed...
Rory
@Rory Hi, Yes you are right. I've changed a lot to my schema and now I am going to start from beginning and finish in once I hope. Rory. I will be really glad if you can also let me know about your solutions for these 3 questions.Thanks for the contribution !
Kubi
@Rory: By "nested select" I assume you mean the derived table. This is the way you should be writing queries when you need to return aggregates as well as description columns. In some cases the results might be equivalent to writing `GROUP BY id, name, other_columns, ...`, but the latter is a functionally incorrect definition of the results and could end up hurting performance. When I see those extra columns in the `GROUP BY` I consider it a SQL code smell - usually somebody wrote it just shut the compiler up.
Aaronaught
@Aaron: You make an interesting point. I'll admit that my formal SQL training amounts to 1 week some 5 years ago, but I find it strange that I've never encountered similar examples in all that time (even in MSDN samples). This certainly warrants some more investigation, thanks very much for the insight :)
Rory
@Kubi: I've already given my +1 to this answer, it fills the requirements for the question with the schema you have provided. It wouldn't be fair to post a second answer because the queries would be almost identical (you might find another way to skin a cat which could be faster or slower, easier or harder, but at the end of the day you still have a skinned cat). You've asked for examples, and since you are reviewing your schema, these answers serve as just that: they may no longer be valid for your new schema, but they are the perfect examples of how to get the real answers.
Rory
@Rory: It's true, you rarely see that in examples/docs. In practice it's rare to have to write a query that includes both aggregates and non-aggregated, non-primary-key columns. If they wanted to demonstrate these then they'd be faced with either providing a complicated, confusing example, or a simpler but badly-written query. If we took out branch name/address and just returned id, the query would be a lot simpler. Similarly if the description columns were numbers then we could simply wrap them in `MIN`. But the requirements state neither so here we are...
Aaronaught