MySQL create a sum number if row exists in table - mysql

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.

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.

How to use having count less than another table

I am having trouble with my MySQL query. I want to show the id FROM one table using left join 3 another table.
What do I want is :
Show id IS NULL in 1 of another table
And having COUNT() < (get column in another table)
I tried this but it is still wrong:
SELECT p.id FROM penerimaan AS p
LEFT JOIN perangkat AS per ON per.id_penerimaan=p.id
LEFT JOIN permintaan AS pa ON pa.id=p.id_permintaan
LEFT JOIN konfirmasi_permintaan AS k ON k.id_permintaan=pa.id
WHERE per.id_penerimaan IS NULL
GROUP BY p.id
HAVING COUNT(per.id_penerimaan) < k.jumlahConfirm //how to get column in another table
ORDER BY p.id ASC
the table I have
table permintaan
id jumlah status
2 3 Confirmed
3 5 Confirmed
-----------------------------------------------
table penerimaan
id id_permintaan date
1 2 2017-07-12
2 3 2017-08-12
-----------------------------------------------
table konfirmasi_permintaan
id id_permintaan jumlahConfirmed
1 2 3
2 3 3
-----------------------------------------------
table perangkat
id id_penerimaan serial type
1 1 766544 SG90D-08-AS
2 1 552411 SLM2008T-EU
3 1 552411 SLM2008T-TU
4 2 561434 SG95-24-AS
I desired result like this
id_penerimaan
2
though id_penerimaan in table perangkat IS NULL but still show, because count(perangkat.id_penerimaan) is 2 in table perangkat less than jumlahConfirm in table konfirmasi_permintaan
Thank you
Removing WHERE per.id_penerimaan seems to solve the problem. You also have to add k.jumlahConfirmed to the SELECT list, because HAVING can only access values in the select list, not columns from the original tables.
SELECT p.id, k.jumlahConfirmed FROM penerimaan AS p
LEFT JOIN perangkat AS per ON per.id_penerimaan=p.id
LEFT JOIN permintaan AS pa ON pa.id=p.id_permintaan
LEFT JOIN konfirmasi_permintaan AS k ON k.id_permintaan=pa.id
GROUP BY p.id
HAVING COUNT(per.id_penerimaan) < k.jumlahConfirmed
DEMO

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 Join tables with no unique id on multiple columns

I have the following tables:
Table Main:
TestNumber PassageNumber QuestionNumber
1 1 1
1 1 2
1 1 3
1 2 1
1 2 2
1 2 3
Table Child:
TestNumber PassageNumber QuestionNumber User SelectedAnswer
1 1 1 X A
1 2 2 X B
I want to show the data in main table that is not in child table based on a test number and user. So the results i am looking for are the following where the rows from main table are the ones NOT in child:
TestNumber PassageNumber QuestionNumber
1 1 2
1 1 3
1 2 1
1 2 3
I have tried the following query and variations with no luck:
SELECT a.passagenumber, a.questionnumber FROM Main a left outer join
Child b on a.testnumber=b.testnumber where b.user = 'X'
and b.testnumber=1 and a.testnumber=1 and b.selectedanswer is not null
I understand if i had a unique id this would be easy to solve but in this case that is not an option. Any help would be much appreciated.
I thin you can use not in
SELECT a.passagenumber, a.questionnumber FROM Main a
where ( a.testnumber, a.passagenumber, a.questionumber)
not in ( select b.testnumber, b.passagenumber, b.questionumber
from Child b where b.user = 'X' )
Can you join the tables on the three fields, using left join and where the child table value is null?
SELECT p.TestNumber
,p.PassageNumber
,p.QuestionNumber
FROM Parent p
LEFT JOIN Child c ON c.TestNumber = p.TestNumber
AND c.PassageNumber = p.PassageNumber
AND c.QuestionNumber = p.QuestionNumber
AND c.[User] = 'X'
WHERE c.TestNumber IS NULL;

Case or if on mySQL query

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