SQL about combine row values - mysql

I have a question about a SQL statement for an existing database table:
SELECT * FROM TABLENAME
gives a result with the columns: id, lead_id, field_number and value.
ID | LEAD_ID | FIELD_NUMBER | VALUE
1 | 1 | 1 | yes
2 | 1 | 2 | a#mail.com
3 | 2 | 1 | yes
4 | 2 | 2 | b#mail.com
5 | 3 | 1 | no
6 | 3 | 2 | c#mail.com
Is it possible to split the value column in two different columns?
expected result:
LEAD_ID | YES_OR_NO | EMAIL
1 | yes | a#mail.com
2 | yes | b#mail.com
3 | no | c#mail.com
I can't use the MAX() argument right?

Use a SELF JOIN and define the FIELD_NUMBER for each self join (if using more than just the two in the example).
SELECT t.LEAD_ID, t.value AS YES_OR_NO, sub.VALUE AS EMAIL
FROM TABLENAME t
INNER JOIN TABLENAME sub ON t.LEAD_ID = sub.LEAD_ID AND sub.FIELD_NUMBER !=1
WHERE t.FIELD_NUMBER = 1
Output
LEAD_ID YES_OR_NO EMAIL
1 yes a#mail.com
2 yes b#mail.com
3 no c#mail.com
SQL Fiddle: http://sqlfiddle.com/#!9/1587e3/3/0

You could use a self join
select a.LEAD_ID, a.VALUE as YES_OR_NOT, b.VALUE as EMAIL
from TABLENAME a
inner join TABLENAME b on a.LEAD_ID = b.LEAD_ID and b.field_number = 2
where a.field_number = 1

Like:
SELECT DISTINCT LEAD_ID
, CASE WHEN FIELD_NUMBER = 1 THEN VALUE END AS 'YES_OR_NO'
, CASE WHEN FIELD_NUMBER = 2 THEN VALUE END AS 'EMAIL'
FROM TABLENAME
No Need for a self join

Related

SQL Difference between two row in group by

I have a table records of store id, processing batch id and start time as follows:
|store_id | batch_id | process_start_time |
| A | 1 | 10 |
| B | 1 | 40 |
| C | 1 | 30 |
| A | 2 | 400 |
| B | 2 | 800 |
| C | 2 | 600 |
| A | 3 | 10 |
| B | 3 | 80 |
| C | 3 | 90 |
Here, rows needed to be grouped by batch_id and time_taken is difference of process_start_time of store A and store C.
So, the expected result would be:
batch_id | time_taken
1 | 20
2 | 200
3 | 80
I tried to do something like:
select batch_id, ((select process_start_time from records where store_id = 'C') - (select process_start_time from records where store_id = 'A')) as time_taken
from records group by batch_id;
But couldn't figure out to select specific rows in that particular group.
Thank you for looking into!
Update: the process_start_time column not necessarily max for store C
You seem to want conditional aggregation and arithmetic:
select batch_id,
(max(case when store_id = 'C' then process_start_time end) -
min(case when store_id = 'A' then process_start_time end)
) as diff
from records
group by batch_id;
You can try a self join.
SELECT r1.batch_id,
r1.process_start_time - r2.process_start_time time_taken
FROM records r1
INNER JOIN records r2
ON r1.batch_id = r2.batch_id
WHERE r1.store_id = 'C'
AND r2.store_id = 'A';
Here's another answer. This is using two instances of the records table and we link them up with where clauses and exists as follows:
select a.batch_id,
c.process_start_time - a.process_start_time as time_taken
from records a,
records c
where a.store_id = 'A'
and c.store_id = 'C'
and exists (
select 1
from records x
where x.batch_id = a.batch_id
and x.batch_id = c.batch_id
);
SELECT DISTINCT
store_a.batch_id,
store_c.process_start_time - store_a.process_start_time AS 'time_taken'
FROM records store_a
INNER JOIN records store_c
ON store_a.batch_id = store_c.batch_id
AND store_c.store_id = 'C'
AND store_a.store_id = 'A'

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 join only if a field has a particular value

I am trying to fetch a table on certain conditions with join. My table is:
tab_registrations
--------------------------------------------
reg_id |familyid| familyname | parent_id |
| | | |
-------|--------|-------------|-----------|
1 | 2 | null | null |
-------|--------|-------------|-----------|
2 | others | abc | 3 |
-------|--------|-------------|-----------|
3 | 3 | null | null |
-------|--------|-------------|-----------|
4 | others | def | 2 |
-------|--------|-------------|-----------|
tab_family
-------------------------------------
family_id | family_name | parent_id |
| | |
-------------------------------------
1 | tyu | 0 |
-------------------------------------
2 | xyz | 1 |
-------------------------------------
3 | mno | 2 |
-------------------------------------
I want to join these tables on:
if tab_registrations.family not equal to null, then select corresponding parent_id from tab_family
SELECT tab_registration.*,tab_family.family_id,tab_family.parent_id
FROM `tab_registration`
join tab_family on tab_registration.family_id = tab_family.family_id
WHERE reg_id = 1
if tab_registrations.family is equal to 'others', then select tab_registrations.familyname and tab_registrations.parent_id
When I try the above query if tab_registrations.family = 'others', no rows fetched
How can I achieve this? Can anyone help me?
Change to LEFT JOIN with the condition that tab_registration.familyid is not equal to others. Also, you can use conditional CASE..WHEN statements to get the familyname and parent_id values.
SELECT tr.*,
CASE WHEN tr.familyid = 'others' THEN tr.familyname
ELSE tf.family_name
END AS familyname,
CASE WHEN tr.familyid = 'others' THEN tr.parent_id
ELSE tf.parent_id
END AS parent_id
FROM tab_registration tr
LEFT JOIN tab_family tf
ON tr.family_id = tf.family_id AND
tr.familyid <> 'others'
WHERE tr.reg_id = 1
For multi-table queries, it if preferable to use Aliasing for code clarity and readability.
may be useful this query
SELECT tr.*,tf.family_id,tf.parent_id,
IF(tr.familyid='others',tr.familyname,tf.family_name) as fname
IF(tr.familyid='others',tr.parent_id,tf.parent_id) as parentId
FROM `tab_registration` tr
left join tab_family tf on tr.family_id = tf.family_id

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

Exctract specific data with self-check

I have this kind of table with data:
+-------------+------------+------------+
| Name | Test1 | Test2 |
+-------------+------------+------------+
| A | 1 | 1 |
+-------------+------------+------------+
| A | 1 | 1 |
+-------------+------------+------------+
| A | 0 | 2 |
+-------------+------------+------------+
| B | 1 | 1 |
+-------------+------------+------------+
| B | 2 | 1 |
+-------------+------------+------------+
| C | 1 | 1 |
+-------------+------------+------------+
| C | 1 | 1 |
+-------------+------------+------------+
I need to get all names that have all in field test1 and test2 values 1.
In this case output should be like:
+-------------+------------+
| C | PASS |
+-------------+------------+
Because all records whose name is C and have Test1=1 and Test2=1 are passed,
For example record A cannot pass because one of row have Test1=0 and Test=2.
Same is for B. B have only one record with Test1=1 and Test2=1 but the next record for B have Test1=2 and Test2=1.
How to make query that can extract those data? Or this is better to solve through code?
Combine both Test1 and Test2 columns with UNION.
Then select the name which having both minimum and maximum test value as 1.
Query
select name,'PASS' as `status`
from
(
select name,test1 as test
from tests
union all
select name,test2 as test
from tests
)t
group by name
having max(t.test) = 1
and min(t.test) = 1;
SQL Fiddle
My idea is to first choose names that do not fill your rule and then select all remaining ones. Is this your logic?
select distinct name, 'PASS' from table
where name not in
(select name from table where test1 <> 1 or test2 <> 1);
SELECT DISTINCT name, "PASS" FROM yourtable WHERE test1 = 1 AND test2=1