Case or if on mySQL query - mysql

I have tables like:
timeline
id data_id pattern
1 1 add_card
2 1 add_post
3 2 upd_card
4 2 upd_post
card
id name parent
1 cname1 1
2 cname2 4
3 cname3 5
post
id name parent
1 pname1 8
2 pname2 9
3 pname3 3
i need form result
** result timeline**
id data_id pattern name parent
1 1 add_card cname1 1
2 1 add_post pname1 8
3 2 upd_card cname2 4
4 2 upd_post pname2 9
My ideas in only IF or CASE statement
SELECT id, data_id, pattern
(CASE parent
WHEN ‘add_card’ THEN (SELECT name.card,parent.card FROM card WHERE data_id.timeline = id.card)
WHEN ‘add_card’ THEN (SELECT name.post,parent.post FROM post WHERE data_id.timeline = id.post)
END)
FROM timeline
but this is not right syntax, this is just my guess how it would look!

Use LEFT OUTER JOINs to selectively join in the card or post table and COALESCE to get the resulting parent/name info in one column:
SELECT timeline.id, timeline.data_id, timeline.pattern,
COALESCE (post.name, card.name) AS name,
COALESCE (post.parent, card.parent) AS parent
FROM timeline
LEFT OUTER JOIN card ON timeline.data_id = card.id
AND timeline.pattern IN ('add_card', 'upd_card')
LEFT OUTER JOIN post ON timeline.data_id = post.id
AND timeline.pattern IN ('add_post', 'upd_post')
You can see the result here: http://sqlfiddle.com/#!2/76d8cb/1/0
The result doesn't match your example result, but your example result appears to be inconsistent with your data, so I think what this does is what you meant. (Update: I've now edited your example results to be consistent with your example tables.)

You appear to be checking for the same value and doing 2 different things.
However I think you probably want something like this:-
SELECT timeline.id, timeline.data_id, timeline.pattern
CASE
WHEN timeline.pattern = 'add_card' THEN card.name
WHEN timeline.pattern = 'add_card' THEN post.name
ELSE NULL
END,
CASE
WHEN timeline.pattern = 'add_card' THEN card.parent
WHEN timeline.pattern = 'add_card' THEN post.parent
ELSE NULL
END
FROM timeline
LEFT OUTER JOIN card timeline.data_id = card.id
LEFT OUTER JOIN post timeline.data_id = post.id

Related

Concatenate two tables by or

I have these tables
One text has many-many items writer,line
ttext_obj table
test obj
1 text1
2 text2
3 text3
4 text4
text_obj_writers table
text writer
1 2
2 3
2 4
text_obj_line table
text line
1 2
4 3
1 4
So, I want to pick up the rows of text_obj which have at reast one writer or one line.
For now I made code like this .
The text_obj id which has at least one write
SELECT text.id FROM `text_obj` text
inner join text_obj_writers writer
on writer.obj_id = text.id group by text.id
//it returns
1
2
The text_obj id which have at least one line
SELECT text.id FROM `text_obj` text
inner join text_obj_lines line
on line.obj_id = text.id group by text.id
//it returns
1
4
But I want to take or of these
1
2
4
How can I concatenate two tables by or ?
Use exists:
select o.*
from text_obj o
where exists (select 1
from text_obj_writers tow
where tow.obj_id = o.id
) or
exists (select 1
from text_obj_lines tol
where tol.obj_id = o.id
) ;
This is much better than using aggregation, because you do not need to remove duplicates after joining the tables together.
You could use exists:
select o.*
from ttext_obj o
where
exists (select 1 from text_obj_writers writer w where w.obj_id = o.id)
or exists (select 1 from text_obj_lines l where l.obj_id = o.id)
You can do it with UNION which will also remove duplicates:
select obj_id id from test_obj_writers
union
select obj_id id from test_obj_line
I assume that all obj_ids of both tables exist in the table text_obj.

Two tables with all rows including allocated based on id

Im trying to make this generic as it might help others in the future.
For an example i have two tables one with books and the other is the user with which book they have read, So ide like to display all the books and include a temporary column value as a (yes / no or 0/1), i have tried a join but the ( WHERE user_id = 3) clause only then return the one row and not all the other rows.
book.book_id book.book_name
10 Book 1
11 Book 2
12 Book 3
-------------
user.user_id user.book_id
1 10
1 12
2 11
3 12
Desired output:
user_id book_id temp_col_read
3 10 0 // yes, on or null
3 12 1 // or yes
3 13 0
This is actually quite simple. In the event that a user could read a book multiple times, I would go with exists in the select:
select b.*,
(case when exists (select 1
from reads r
where r.book_id = b.book_id and r.user_id = 3
)
then 1 else 0
end) as user_read_book
from book b;
In MySQL, the case is not strictly necessary because a boolean expression is treated as 0/1 in many contexts:
select b.*,
(exists (select 1
from reads r
where r.book_id = b.book_id and r.user_id = 3
) as user_read_book
from book b;
You can use a left join and where the join is unresolved then is not read
select
user.user_id
, book.book_id
, case
when book.book_id is null
then 'NO' else 'YES'
end as temp_col_read
from book
left join user on user.book_id = book.book_id

MySQL create a sum number if row exists in table

I have a statement like so:
select * from category a
inner join category b on a.row=b.relatedRow
inner join category c on b.row=c.relatedRow where a.row=?
I would like to get the number of "levels" like so:
If a has rows, level=1, If b has rows, level=2, If c has rows, level=3.
How can I do this?
Example
row, relatedRow
1,null
2,1
3,2
4,3
5,2
6,5
So, 1 is not related to any row, 2 is related to 1, 3 is related to 2 and so on...
If row=1, level 1 exists since 1 exists
level 2 exists since 2 is related to 1
level 3 exists since 3 and 5 is related to 2
level 4 exists since 6 is related to 5 and 4 is related to 3
Therefore the this tree goes down 4 levels.
try with something along the lines:
select
sum(case when not b.relatedRow is null then 1 else 0 end) as level1_total
sum(case when not c.relatedRow is null then 1 else 0 end) as level2_total
from category a
left join category b on a.row=b.relatedRow
left join category c on b.row=c.relatedRow where a.row=?
of course, you can modify the conditions in the case to suit your definition of has rows
select
max(
case
when c.relatedRow is not null then 3
when b.relatedRow is not null then 2
else 1
end
) as "levels"
from
A a
left outer join B b on b.relatedRow = a.row
left outer join C on c.relatedRow = b.row
Now seeing the edit to the question, I hope you see this pattern can be extended to a 4th level and beyond. If you add a where clause to do any filtering make sure that you only add conditions against A or you'll mess up the outer joins.

Mysql query from one table with different conditions

I would like to calculate every item's vote count for this table:
user item vote
--------------------------------
4 1 left
4 2 left
2 2 right
3 2 left
1 3 right
The result must be like this:
item | left_vote | right_vote
1 1 0
2 2 1
3 0 1
I've tried to use queries like this:
SELECT t.item, count(vote) as left_count, t.right_count from (SELECT count(vote) as right_count, item from view where vote = 'right') as t, view where vote = 'left';
But it doesn't work. I think that I have to use join with subquery.
Is it real with mysql?
In MySQL you can just use conditional aggregation:
select item, sum(vote = 'left') as left_vote, sum(vote = 'right') as right_vote
from votes v
group by item;
You don't need a join or subquery for this.

sql query has few rows which are duplicate

I want the value for users with the right ref and also users with extra value 1.
I have the following query but its not giving unique rows ,its giving duplicate rows.How do I resolve that ? I really appreciate any help.
basically its repeating values of ref.id if tab2.user=1 which repeats 4,6 row again in the final query I dont want both but only one as ref.id=0 does not exist but I want extra=1.
SELECT * FROM
tab1,tab2
WHERE tab1.ref=tab2.ref
AND tab1.to_users = 1
OR users.extra=1;
Tab1
sno users ref extra
1 1 4 1
2 2 5 0
3 3 0 1
4 1 0 1
5 2 5 0
6 3 0 1
Tab2
ref ad user
4 A 1
5 B 2
6 C 1
After your last comment:
SELECT sno, users, tab1.ref, extra, max(ad) as ad
FROM
tab1,tab2
WHERE tab1.ref=tab2.ref
AND tab1.users = 1
OR tab1.extra=1
group by sno, users, ref, extra;
This will max the ad column (alternatively you can use min - all depends on your requirements)
example: http://sqlfiddle.com/#!2/1837e/25
You will probably need to use join
SELECT * FROM
tab1 LEFT JOIN tab2 ON tab1.ref=tab2.ref
WHERE tab1.users = 1 OR tab1.extra=1;
GROUP BY tab1.users
edit: added group by
GROUP BY users, give below syntax a try
SELECT * FROM
tab1,tab2
WHERE tab1.ref=tab2.ref AND (tab1.users = 1 OR tab1.extra=1)
GROUP BY users;
Its more common to also join tables than they way you write your syntax,
SELECT * FROM
tab1 JOIN tab2 ON tab1.ref=tab2.ref AND (tab1.users = 1 OR tab1.extra=1)
GROUP BY users;
You should use parenthesis to change the priority of And and OR :
SELECT * FROM
tab1,tab2
WHERE tab1.ref=tab2.ref
AND (tab1.to_users = 1
OR users.extra=1);
otherwise it will be run like this :
SELECT * FROM
tab1,tab2
WHERE (tab1.ref=tab2.ref
AND tab1.to_users = 1 )
OR users.extra=1;
UPDFATE:
I thought you were looking for rows with same reference where to_users is one or extra is one. Then as Krzysztof Rosiński says LEFT JOIN should do the trick but there's no need to use GROUP BY and perhaps you should use DISTINCT something like this :
SELECT DISTINCT * FROM
tab1 LEFT JOIN tab2 ON tab1.ref=tab2.ref
WHERE tab1.users = 1 OR tab1.extra=1;