Multiple SQL rows merge into single row if the id is same - mysql

What is the mysql I need to achieve the result below given this table:
table:
+----+-------+--------------
| name| id | items | vol
+----+-------+---------------
| A | 1111 | 4 | 170
| A | 1111 | 5 | 100
| B | 2222 | 6 | 200
| B | 2222 | 7 | 120
+----+-------+-----------------
Above table is the result of union query
SELECT * FROM imports
union all
SELECT * FROM exports
ORDER BY name;
I want to create a temporary view that looks like this
desired result:
+----+---------+---------+-------------------
| name| id | items | vol | items1 | vol2
+-----+--------+-------+--------------------
| A | 1111 | 4 | 170 | 5 | 100
| B | 2222 | 6 | 200 | 7 | 120
+----+---------+---------+-------------------
any help would be greatly appreciated! -Thanks

Use PIVOT:
SELECT name,id,
SUM( CASE WHEN typ = 'imports' THEN items ELSE 0 END) as imports_items,
SUM( CASE WHEN typ = 'imports' THEN vol ELSE 0 END) as imports_vol,
SUM( CASE WHEN typ = 'exports' THEN items ELSE 0 END) as exports_items,
SUM( CASE WHEN typ = 'exports' THEN vol ELSE 0 END) as exports_vol
FROM (
SELECT 'imports' as typ, t.* FROM imports t
union all
SELECT 'exports' as typ, t.* FROM exports t
) x
GROUP BY name,id
ORDER BY name;

This should give you the table you are looking for:
SELECT
a.name,
a.id,
a.items,
a.vol,
b.items as items2,
b.vol as vol2
FROM imports a
INNER JOIN exports b on b.id = a.id;

Related

selecting only newest row with specific value

Table:
person | borrow_date | is_borrowed | SN | date | id
1 | 2019-01-10...| 1 | 20 |2019-01-10...| 6
3 | 2019-01-09...| 3 | 10 |2019-01-09...| 5
1 | 2019-01-08...| 1 | 10 |2019-01-08...| 4
2 | 2019-01-08...| 1 | 10 |2019-01-08...| 3
1 | NULL | 2 | 20 |2019-01-07...| 2
1 | NULL | 2 | 10 |2019-01-07...| 1
My wanted output is to select newest rows where "is_borrowed" equals 1 and grouped by SN, so that when the query is executed with person=2 or person=3 then it would retrieve empty set. Whereas for person=1 it would give back two rows.
Wanted output (where person=1):
person | borrow_date | is_borrowed | SN | date |id
1 | 2019-01-10...| 1 | 20 | 2019-01-10...|6
1 | 2019-01-08...| 1 | 10 | 2019-01-08...|4
Wanted output (where person=2):
EMPTY SET
Wanted output (where person=3):
EMPTY SET
This is my current query and it sadly doesn't work.
SELECT a.SN, a.is_borrowed,a.max(date) as date, a.person
FROM table a
INNER JOIN (SELECT SN, MAX(date) as date, osoba from table where person like
"2" group by SN) as b
ON a.SN=b.SN and a.date=b.date
WHERE a.person like "2" and a.is_borrowed=1
If I correctly understood you from the question and the comment you made under it, here's one way to do it without specifying the person:
select *
from TableName as p
inner join (select max(borrow_date) as borrow_date,
SN
FROM TableName
where is_borrowed = 1
group by SN) as p2
on p.borrow_date = p2.borrow_date and p.SN = p2.SN
This should give you the result you're looking for. Here's a demo.
Note that I had to change the borrowed_date values in the table since yours contain hours and minutes while I didn't add those.
You can always specify it for each person by adding a where clause after the join.
select p.person,
p.borrow_date,
p.is_borrowed,
p.SN,
p.date,
p.id
from TableName as p
inner join (select max(borrow_date) as borrow_date,
SN
FROM TableName
where is_borrowed = 1
group by SN) as p2
on p.borrow_date = p2.borrow_date and p.SN = p2.SN
where p.person = '1'
Output:
person | borrow_date | is_borrowed | SN | date | id
1 | 2019-01-10 | 1 | 20 | 2019-01-10 | 6
1 | 2019-01-08 | 1 | 10 | 2019-01-08 | 4
While where p.person = '2' and where p.person = '3' will return empty sets.

Mysql count rows and rows with null

Hy Guys,
I have a table like that:
+----+------+
| id | grade|
+----+------+
| 1 | 1 |
| 2 | 1 |
| 3 | 2 |
| 4 | NULL |
| 5 | NULL |
| 6 | NULL |
+----+------+
Where
1 Bad
2 Good
3 Very Good
Im trying to get a result something like that:
+--------------+------+
| grade | count|
+--------------+------+
| "Bad" | 2 |
| "Good" | 1 |
| "Very Good" | 0 |
| "Not Ranked" | 3 |
+--------------+------+
Im tryng with count but no success
You can use CASE to change the value of grade ibno your desired string value,
SELECT CASE WHEN grade = 1 THEN 'Bad'
WHEN grade = 2 THEN 'Good'
WHEN grade = 3 THEN 'Very Good'
ELSE 'Not Ranked'
END as grade,
COUNT(IFNULL(grade, 0)) as `count`
FROM TableName
GROUP BY CASE WHEN grade = 1 THEN 'Bad'
WHEN grade = 2 THEN 'Good'
WHEN grade = 3 THEN 'Very Good'
ELSE 'Not Ranked'
END
Since you wanted to display all values, you need to create a subquery that returns all values and join to your table using LEFT JOIN.
SELECT a.grade,
COUNT(b.id)
FROM
(
SELECT 1 id, 'Bad' grade UNION ALL
SELECT 2 id, 'Good' grade UNION ALL
SELECT 3 id, 'Very Good' grade UNION ALL
SELECT 999 id, 'Not Ranked' grade
) a
LEFT JOIN TableName b ON a.id = IFNULL(b.grade, 999)
GROUP BY a.grade
Here's a Demo.
If you can use the numbers, and match them with the string later, then this could be a solution:
SELECT grade, count(*) as id FROM tbl GROUP BY grade
source

SQL Query - Filter with different type

I'm very stuck to create my sql query. If someone can help me or advice me.
I have a group with an Arraycollection of Filters:
Group :
-------------------------
| id | name |
|------------------------
| 1 | group1 |
|------------------------
| 2 | group2 |
|------------------------
| 3 | group3 |
|------------------------
| 4 | group4 |
|------------------------
Filter :
------------------------------------
| id | group_id | Type| Value |
|-----------------------------------
| 1 | 1 | 1 | 15 |
|------------------------------------
| 2 | 1 | 2 | 25-34 |
|------------------------------------
| 3 | 1 | 2 | 35-44 |
|------------------------------------
| 4 | 2 | 2 | 25-34 |
|------------------------------------
| 5 | 2 | 2 | 35-44 |
|------------------------------------
| 6 | 3 | 1 | 11 |
|------------------------------------
| 7 | 3 | 1 | 15 |
|------------------------------------
For example, I need to filter my group with those critere:
filter.type = 1 and filter.value = 11
filter.type = 2 and filter.value in ('25-34','35-44')
filter.group_id = null
The result must be :
-------------------------
| id | name |
|------------------------
| 2 | group2 |
|------------------------
| 3 | group3 |
|------------------------
| 4 | group4 |
|------------------------
Group1 is not taken because the unique value for type=1 is different of 11. Group3 is taken because he has one result with the value=11 when type=1.
Thanks a lot for your help.
Have tries something like that :
SELECT *FROM group c0_
LEFT JOIN group_filter c1_ ON c0_.id = c1_.group_id
WHERE
(
c0_.public = '1' AND c0_.state = '4'
) AND(
(
c1_.id IN(
SELECT
c2_.id
FROM
group c3_,
group_filter c2_
WHERE
(
c2_.id IN(
SELECT
c4_.id
FROM
group c5_,
group_filter c4_
WHERE
c4_.type = 1 AND c4_.value = 11
)
) AND(
(
c2_.type = 2 AND c2_.value IN('18-24', '25-34')
)
)
)
) OR c1_.group_id IS NULL
)
GROUP BY
c0_.id
LIMIT 25 OFFSET 0
select * from t_group INNER JOIN (
SELECT
t_group.id as id ,
GROUP_CONCAT(case
when t_filter.t_type=1 and t_filter.t_value='11' THEN 'A'
when t_filter.t_type=1 and t_filter.t_value<>'11' THEN 'B'
when t_filter.t_type=2 and (t_filter.t_value ='25-34' or t_filter.t_value ='35-44') then 'C'
when t_filter.group_id is null then 'D'
else 'E'
END) str
FROM
t_group
LEFT JOIN t_filter ON t_group.id = t_filter.group_id
GROUP BY
t_group.id) t ON t_group.id=t.id
where (LOCATE('B',t.str)=0 OR LOCATE('A',t.str)>0) AND LOCATE('E',t.str)=0
first,i query all the probably result,and contact to a string accroding to the group,then except the result i do not want
If I don't misunderstand your criterias, below SQL works. (please remove the comments in the SQL codes before try at your client)
select distinct g1.id, g1.name
from groups g1 left join filters f1 on g1.id=f1.group_id
where
(
f1.type=1 and f1.value=11
and exists (select 1 from filters f2 where f2.group_id=f1.group_id and f2.type=2 and f2.value in ('25-34','35-44'))
) #If type=1 and value=11, then must exists same group with type=2 and value in ('25-34', '35-44')
or (
f1.type=1 and f1.value<>11
and not exists (select 1 from filters f2 where f2.group_id=f1.group_id and f2.type=2 and f2.value in ('25-34','35-44'))
) #If type=1 and value=11, then doesn't allow same group exists type=2 and value in ('25-34','35-44')
or not exists (select 1 from filters f2 where f2.group_id=f1.group_id and f2.type=1)
#If not exists same group have type=1 (including null) then return true
If the table1 name is Groups and table2's Filters ,
For the result:
-------------------------
| id | name |
|------------------------
| 2 | group2 |
|------------------------
| 3 | group3 |
|------------------------
| 4 | group4 |
|------------------------
the query will be:
SELECT g.id,g.name
FROM groups AS g
LEFT OUTER JOIN filters AS f
ON f.group_id=g.id
WHERE ( g.type IN(1,2) AND (g.value IN ('25-34','35-44') ) ) ;
Please correct me if i'm wrong

Join two table and count, avoid zero if record is not available in second table

I have following tables products and tests.
select id,pname from products;
+----+---------+
| id | pname |
+----+---------+
| 1 | prd1 |
| 2 | prd2 |
| 3 | prd3 |
| 4 | prd4 |
+----+---------+
select pname,testrunid,testresult,time from tests;
+--------+-----------+------------+-------------+
| pname | testrunid | testresult | time |
+--------+-----------+------------+-------------+
| prd1 | 800 | PASS | 2017-10-02 |
| prd1 | 801 | FAIL | 2017-10-16 |
| prd1 | 802 | PASS | 2017-10-02 |
| prd1 | 803 | NULL | 2017-10-16 |
| prd1 | 804 | PASS | 2017-10-16 |
| prd1 | 805 | PASS | 2017-10-16 |
| prd1 | 806 | PASS | 2017-10-16 |
+--------+-----------+------------+-------------+
I like to count test results for products and if there is no result available,for a product just show a zero for it. something like following table:
+--------+------------+-----------+----------------+---------------+
| pname | total_pass | total_fail| pass_lastweek | fail_lastweek |
+--------+------------+-----------+----------------+---------------+
| prd1 | 5 | 1 | 3 | 1 |
| prd2 | 0 | 0 | 0 | 0 |
| prd3 | 0 | 0 | 0 | 0 |
| prd4 | 0 | 0 | 0 | 0 |
+--------+------------+-----------+----------------++--------------+
I have tried different queries like following, which is just working for one product and is incomplete:
SELECT pname, count(*) as pass_lastweek FROM tests where testresult = 'PASS' AND time
>= '2017-10-11' and pname in (select pname from products) group by pname;
+-------------+---------------+
| pname | pass_lastweek |
+-------------+---------------+
| prd1 | 3 |
+-------------+---------------+
it looks so basic but still I am unable to write it, any idea?
Use conditional aggregation. The COUNT function count NULL values as zeros automatically, therefore, there is no need to take care of that.
select p.pname,
count(case when testresult = 'PASS' then 1 end) as total_pass,
count(case when testresult = 'FAIL' then 1 end) as total_fail,
count(case when testresult = 'PASS' and time >= curdate() - INTERVAL 6 DAY then 1 end) as pass_lastweek ,
count(case when testresult = 'FAIL' and time >= curdate() - INTERVAL 6 DAY then 1 end) as fail_lastweek ,
from products p
left join tests t on t.pname = p.pname
group p.id, p.pname
Generally, you need to LEFT JOIN the first table with the second one before you group. The join will give you a row for each product (even if there are no test results to join it to; INNER JOIN would exclude products with no associated tests) + an additional row for each test result (beyond the first). Then you can group them.
SELECT products.*, tests.* FROM products
LEFT JOIN tests ON products.pname = tests.pname
GROUP BY products.id
Also, I would strongly recommend using a product_id column in the tests table, rather than using pname (if a products.pname changes, your whole DB breaks unless you also update the pname field in kind for every test result). The general query would then look like this:
SELECT products.*, tests.* FROM products
LEFT JOIN tests ON products.id = tests.product_id
GROUP BY products.id
I used 2 queries , the first with conditional count and the second one is to change all null values into 0 :
select pname,
case when total_pass is null then 0 else total_pass end as total_pass,
case when total_fail is null then 0 else total_fail end as total_fail,
case when pass_lastweek is null then 0 else pass_lastweek end as pass_lastweek,
case when fail_lastweek is null then 0 else fail_lastweek end asfail_lastweek from (
select products.pname,
count(case when testresult = 'PASS' then 1 end) as total_pass,
count(case when testresult = 'FAIL' then 1 end) as total_fail,
count(case when testresult = 'PASS' and time >= current_date -7 DAY then 1 end) as pass_lastweek ,
count(case when testresult = 'FAIL' and time >= current_date -7 DAY then 1 end) as fail_lastweek ,
from products
left join tests on tests.pname = products.pname
group 1 ) t1

Combine 2 SELECT statements with filled gaps

I have this SELECT:
SELECT
m.`maschine-name` AS 'byMaschine',
q.`mname` AS 'byMName'
FROM
`qualitaet` q
INNER JOIN
maschinen m ON m.maschine = q.maschine
WHERE
q.`status`='1'
GROUP BY
concat(q.maschine, q.mname)
and get this result:
| maschine-name | mname |
| TYP 1 | 0 |
| TYP 2 | 3 |
| TYP 2 | 4 |
| TYP 3 | 0 |
| TYP 4 | 0 |
see SQL Fiddle here
Then i have nearly the same SELECT with additional COUNT and Datefilter:
SELECT
m.`maschine-name` AS 'byMaschine',
q.`mname` AS 'byMName',
COUNT(*) AS 'total'
FROM
`qualitaet` q
INNER JOIN
maschinen m ON m.maschine = q.maschine
WHERE
q.`created`>=NOW() - INTERVAL 2 DAY
AND
q.`status`='1'
GROUP BY
concat(q.maschine, q.mname)
and get this result:
| maschine-name | mname | total |
| TYP 2 | 3 | 1 |
| TYP 3 | 0 | 2 |
see SQL Fiddle here
The 2nd SELECT doesn't give me all information. I need a mix from both SELECTS
The 2nd SELECT should look like this result:
| maschine-name | mname | total |
| TYP 1 | 0 | 0 |
| TYP 2 | 3 | 1 |
| TYP 2 | 4 | 0 |
| TYP 3 | 0 | 2 |
| TYP 4 | 0 | 0 |
Is it possible to RIGHT JOIN with 2 SELECTS? Or is there another way to get the result?
Use conditional aggregation:
SELECT m.`maschine-name` AS byMaschine, q.`mname` AS byMName,
sum(q.created >= NOW() - INTERVAL 2 DAY) as Total
FROM qualitaet q INNER JOIN
maschinen m
ON m.maschine = q.maschine
WHERE q.status = '1'
GROUP BY q.maschine, q.mname;
Other suggestions:
There is no need to concatenate the grouping columns in the GROUP BY -- unless you really, really intend to do this (which I doubt).
If status is numeric, don't use single quotes for the constant.
Don't use single quotes for column aliases. In fact, your aliases don't need any quotes at all.