views:

114

answers:

3
        int id = 1;
        string chain = "(";
        SqlDataReader dr = SqlHelper.ExecuteReader(string.Format("SELECT a.Id as x, c.Id as y From Friends b INNER JOIN Users a ON b.SenderId = a.Id INNER JOIN Users c ON b.ReceiverId = c.Id WHERE (c.Id = {0} OR a.Id = {0}) AND State = '{1}'", id, "ok"));
        if (dr.HasRows)
            while (dr.Read())
                if (id == int.Parse(dr["y"].ToString()))
                    chain += dr["x"].ToString() + ", ";
                else
                    chain += dr["y"].ToString() + ", ";
        if (chain.Length > 1)
            chain = chain.Substring(0, chain.Length - 2) + ")";
        else
            chain = "(0)";
        // Chain has for example  => (2, 3, 4, 5) => which are the Ids for Users's Friends 
        string str = "SELECT TOP 20 a.*, b.UserName as Sender, c.UserName as Receiver, b.Avatar as SenderPic";
        str += " FROM Events a INNER JOIN Users b ON a.SenderId = b.Id INNER JOIN Users c ON a.ReceiverId = c.Id ";
        str += "WHERE SenderId IN ";
        str += chain;
        str += " OR ReceiverId IN";
        str += chain;
        str += " Order BY Id desc";
        dr = SqlHelper.ExecuteReader(str);

chain considered as the user's friends . does any one know how to execute this query with JOINS !? Thanks a lot ..

+2  A: 

First, try to get rid of pushing parameter values with string.Format into SQL queries. That's a huge security issue.

To your query. I have to admit, I'm a bit lost in your string concatenation :-P, but if I'm right, you want to get the Events and some user information. As far as I can see, SenderId and ReciverId are already your ids. If so, you can completely remove the first SELECT and provide id (as parameter) directly into your second SQL statement like this (only the SQL):

Edit: Tom showed me the missing part (Status=Ok)

SELECT TOP(20) a.* ,b.UserName as Sender ,c.UserName as Receiver ,b.Avatar as SenderPic FROM Events a INNER JOIN Users b ON a.SenderId = b.Id INNER JOIN Users c ON a.ReceiverId = c.Id WHERE a.SenderId = @id OR a.ReceiverId = @id;


Corrected version:

; WITH OkUsers AS (
   SELECT
      u.*
   FROM Users u
      JOIN Friends f ON u.Id = f.SenderId OR u.Id = f.RecipientId
   WHERE
      f.Status = 'Ok'
)
SELECT TOP(20)
   a.*
   ,b.UserName as Sender
   ,c.UserName as Receiver
   ,b.Avatar as SenderPic
FROM Events a 
   INNER JOIN OkUsers b ON a.SenderId = b.Id 
   INNER JOIN OkUsers c ON a.ReceiverId = c.Id 
WHERE
   a.SenderId = @id
   OR a.ReceiverId = @id;

Greets Flo

Florian Reischl
+1 absolutely - concatenating together a SQL command like that is a HUGE GAPING SQL injection hole waiting to be exploited!
marc_s
I think the original had an extra WHERE clause on Friends.State = 'ok' (assuming the state column is defined on the friends table)
Tom Carver
@Tom Carver Thanks for the hint. I updated the SQL statement.
Florian Reischl
I don't have a specific Id to use it in WHERE SenderId = @id OR ReceiverId = @id , instead I have a user Id which I can use it to Get his friends from table "Friends" so I has used "IN" .. ANY Suggestions !?!
Rawhi
You don't need the IN if you work with a single statement (what was your question). Use a DbParameter to provide the @id value from your "id" variable.
Florian Reischl
Florian Reischl Can you give me your msn if you have one !?!?
Rawhi
@Rawhi I don't have one and I don't think this would be a good solution. First, my humpy knowledge would never worth a community with more than 100,000 users, so your solution probably wouldn't be the best. Second, the great thing about communities like this is, the solution for one problem can help many others who find it, here. Anything we would do in a _private_ area would be lost for all the others.
Florian Reischl
A: 

I think that might help:

--step[1]
SELECT DISTINCT CASE
WHEN a.Id = 1 THEN c.ID
WHEN c.Id = 1 THEN a.Id
ELSE
0 
END AS ID
INTO #OkUsers
From Friends b INNER JOIN Users a 
ON b.SenderId = a.Id INNER JOIN Users c 
ON b.ReceiverId = c.Id 
WHERE (c.Id = @id OR a.Id = @id) AND State = 'Ok';

--step[2]
SELECT TOP 20 a.*, b.UserName as Sender, c.UserName as Receiver, b.Avatar as SenderPic
FROM Events a INNER JOIN Users b ON a.SenderId = b.Id INNER JOIN Users c ON a.ReceiverId = c.Id
INNER JOIN #OkUsers ON #OkUsers.ID = SenderId OR #OkUsers.ID = ReceiverId
EXCEPT
SELECT TOP 20 a.*, b.UserName as Sender, c.UserName as Receiver, b.Avatar as SenderPic
FROM Events a INNER JOIN Users b ON a.SenderId = b.Id INNER JOIN Users c ON a.ReceiverId = c.Id
--INNER JOIN #OkUsers ON #OkUsers.ID = SenderId OR #OkUsers.ID = ReceiverId
WHERE SenderId IN (SELECT ID FROM #OkUsers) 
    OR ReceiverId IN(SELECT ID FROM #OkUsers);

DROP TABLE #OkUsers;

Temp tables work good with very large volume of data.Otherwise you can use a memory table.

SubPortal
how can I use this in my code !Also , I've extracted all the identification numbers to all the friends of a member and then put it in a variable of type String to be used with IN Clause in the Second query .. is that a good way or what !?
Rawhi
any sql code that gives you the final result in only one trip to the db is better than doing it with two round trips. Also getting the data from sql server into memory and working with it is better performance than doing it all with sql server, but this is not an always-valid rule because it depends on the volume of data, the application server capacity and the users load...etc
SubPortal
I can't even think how this could be written in a one single sql statement !!! #OkUsers => SELECT => DROP
Rawhi
you can create a stored procedure and put this code inside it and call the procedure from within your C# code.
SubPortal
A: 
SELECT distinct TOP(20) e.*, u1.UserName As Sender,
u2.UserName As Receiver, u1.Avatar AS SenderPic  
FROM Friends f INNER JOIN Users u 
ON(u.Id = f.SenderId OR u.Id = f.ReceiverId) AND State = 'ok' 
INNER JOIN Events e 
ON(f.SenderId = e.SenderId OR f.SenderId = e.ReceiverId 
OR f.ReceiverId = e.SenderId OR f.ReceiverId = e.ReceiverId) 
INNER JOIN Users u1 
ON (e.SenderId = u1.Id) 
INNER JOIN Users u2 
ON (e.ReceiverId = u2.Id) 
WHERE u.Id = @id;
Rawhi