MySQL COUNT of distinct values from two independent columns of a table - mysql

I want to get the COUNT of distinct values from two independent columns of a table.
My table is:
ID CR PB DB CB
-----------------------------
1 1000 1000
2 60000 1000
3 1000 (NULL)
4 1500000 13000
5 60000 12000
6 1000 (NULL)
expected output:
CR PB cnt_crpb DB CB cnt_dbcb
1000 3 1000 2
60000 2 13000 1
1500000 1 12000 1
I have tried to separate both columns CR PB and DB CB in two different tables and joined them using LEFT JOIN but does not give expected output as MySQL does not support FULL OUTER JOIN.
I have also tried using UNION which but gives result in rows.
Any help will be appreciated...
Thanks you.

I think you need to do this using union all:
select max(CRPB) as CRPB, max(CRPB_cnt) as CRPB_cnt, max(DBCB) as DBCB, max(DBCB_cnt) as DBCB_cnt
from ((select (#rn1 := #rn1 + 1) as rn, CRPB, count(CRPB) as CRPB_cnt, NULL as DBCB, NULL as DBCB_cnt
from table t cross join
(select #rn1 := 0) as vars
group by CRPB
) union all
(select (#rn2 := #rn2 + 1) as rn, NULL, NULL, DBCB, count(DBCB) as DBCB_cnt
from table t cross join
(select #rn2 := 0) as vars
group by DBCB
)
) x
group by rn;
This will guarantee results regardless of which list is longest.

Note you need to determine which column will produce more results aka either CR PB or DB CB whichever produces the most results will be the first select you want to do then left join the other. assuming that there is an uneven number of results from the two
SELECT `CR PB`, cnt_crpb, `DB CB`, cnt_dbcb
FROM
( SELECT `CR PB`, COUNT(*) as cnt_crpb, #a := #a + 1 as num_rows_a
FROM test_table
CROSS JOIN (SELECT #a := 0 ) temp
WHERE `CR PB` is not null
GROUP BY `CR PB`
)t
LEFT JOIN
( SELECT `DB CB`, COUNT(*) as cnt_dbcb, #b := #b + 1 as num_rows_b
FROM test_table
CROSS JOIN (SELECT #b := 0)temp1
WHERE `DB CB` is not null
GROUP BY `DB CB`
)t1 ON t1.num_rows_b = t.num_rows_a;
Fiddle Demo

Related

MySQL SUM of LIMIT(3) rows with GROUP BY

Good people, need a bit of a help with MySQL. Tried few solutions online but could get it right.
I have this simple table.
name amount
john | 150
john | 100
john | 100
john | 150
jack | 300
jack | 100
jack | 100
Basically, I have to get the users that have sum of 500 in at least 3 rows(ordered by the highest amount).
The correct answer should only return jack because only he has sum of 500 in 3 records(ordered by highest). Where else john has 500 in total sum, 3 of his highest amounts would only return 400(150+150+100), so the query doesn't return john.
SELECT
*,
SUM(amount) as sums
FROM (SELECT * FROM transfer GROUP BY name ORDER BY amount DESC LIMIT 3) as ttl
GROUP BY name
HAVING sums >= 500
It works fine(no errors at least), but the second select(the one inside the bracket) only returns the first row.
Any help is highly appreciated.
Let me assuming that you have another column that is a unique id. Then you can do this as:
select distinct t1.name
from transfer t1 left join
transfer t2
on t1.name = t2.name and t1.id < t2.id left join
transfer t3
on t1.name = t3.name and t2.id < t3.id
where t1.amount + coalesce(t2.amount, 0) + coalesce(t3.amount, 0) >= 500;
This is not wildly efficient for larger tables. For that, use variables to enumerate the values:
select name
from (select t.*,
(#rn := if(#n = name, #rn + 1,
if(#n := name, 1, 1)
)
) as rn
from transfer cross join
(select #n := '', #rn := 0) params
order by name, amount desc
) t
where rn <= 3
group by name
having sum(amount) >= 500;
This also has the benefit that it does not rely on an id column.

Combining 2 tables to produce 1 output - SQL

I have a query below, and it works.
$query_for_cat3 = "SELECT sum,candidate_no,#curRank := #curRank + 1 AS rank
FROM (SELECT SUM(score) / 5 SUM,candidate_no
FROM SCORE
WHERE category_no='$category_no1'
GROUP BY candidate_no
) a, ( SELECT #curRank := 0 ) r
ORDER BY sum DESC,candidate_no DESC
LIMIT 5";
What I need to do is to combine this query to another table which is named candidates. It has columns candidate_no and candidate_name. I want to produce the candidate_name in respect to their corresponding candidate_no.
Please help. Thanks.
Is this is something you looking for?
SELECT temp.candidate_no,
C.candidatename,
temp.[sum],
temp.rank
FROM candidate C INNER JOIN
(SELECT sum,candidate_no,#curRank := #curRank + 1 AS rank
FROM (SELECT SUM(score) / 5 SUM,candidate_no
FROM SCORE
WHERE category_no='$category_no1'
GROUP BY candidate_no
) a, ( SELECT #curRank := 0 ) r
ORDER BY sum DESC,candidate_no DESC
LIMIT 5) As temp
ON C.candidate_no=temp.candidate_no
You can do a join like this
SELECT
table1.field
table2.field
FROM
table1
LEFT JOIN
table2
ON
table1.field=table2.field
If you want to select data from two table then you need to have primary-key or foreign key Method in both table...like if Candidate_no is in one table as primary key other table as Foreign key..then using this you can access data as::
select * from table_name1 where candidate_no=(select candidate_no from table_name2 where name="Charn");

Need a sequence number for every row in MySQL query

So I found this great use:
SELECT (#row:=#row+1) AS ROW, ID
FROM TableA ,(SELECT #row := 0) r
ORDER BY ID DESC
The #row:=#row+1 works great, but I get the row ordered by the ID.
My tables look more like this:
SELECT (#row:=#row+1) AS ROW, ID , ColA, ColB, ColC
FROM TableA
JOIN TableB on TableB.ID = TableA.ID
JOIN TableC on TableC.ID = TableA.ID
WHERE ID<500
,(SELECT #row := 0) r
ORDER BY ID DESC
Note:
I noticed that if I remove the JOINs I DO get the requested result (In Which ROW is the sequential number of each row, no matter the ORDER BY of ID). The first example works great but for some reaosn, the JOINs mess it up somehow.
so I get this:
ROW | ID
3 15
2 10
1 2
What I am after is:
ROW | ID
1 15
2 10
3 2
Here's the SqlFiddle
So it basically seems that the row number is evaluated before the ORDER BY takes place. I need the ORDER BY to take place after row was given.
How can I achieve that?
Remove the ORDER BY:
SELECT (#row:=#row+1) AS ROW, ID
FROM table1 ,(SELECT #row := 0) r
See SQL Fiddle with Demo
Then if you want to use an ORDER BY wrap the query in another SELECT:
select *
from
(
SELECT (#row:=#row+1) AS ROW, ID
FROM table1 ,(SELECT #row := 0) r
) x
order by row
Or if you leave the ORDER BY on the query, then you can see the way the row number is being applied by simply playing with either DESC or ASC order - See Demo
If you use DESC order
SELECT (#row:=#row+1) AS ROW, ID
FROM table1, (SELECT #row := 0) r
order by id desc;
the results are which appears to be the result you want:
ROW | ID
----------
1 | 15
2 | 10
3 | 2
If you use ASC order:
SELECT (#row:=#row+1) AS ROW, ID
FROM table1 ,(SELECT #row := 0) r
ORDER BY ID;
the results are:
ROW | ID
----------
1 | 2
2 | 10
3 | 15
Edit, based on your change, you should place the row number in a sub-query, then join the other tables:
select *
from
(
SELECT (#row:=#row+1) AS ROW, ID
FROM Table1,(SELECT #row := 0) r
order by ID desc
) x
JOIN Table2
on x.ID = Table2.ID;
See SQL Fiddle with Demo
I dont find any problem with your query
SELECT (#row:=#row+1) AS ROW, ID
FROM table1 ,(SELECT #row := 0) r
order by ID desc
SQL Fiddle demo

MySQL- Query with repetitive fixed id range

I have the following query:
SELECT child.id_catalog_category AS id_category, ancestor.id_catalog_category AS tree
FROM catalog_category AS child
JOIN catalog_category AS ancestor
ON (child.lft BETWEEN ancestor.lft AND ancestor.rgt)
WHERE ancestor.id_catalog_category != 1
ORDER BY id_category ASC, tree ASC
which is a reconstruction of a binary tree for a hierarchical product categories. For one id_category we could have a maximum of 4 "tree" values, as the example shows:
id_category / tree
3 2
3 3
4 2
4 3
4 4
5 2
5 3
5 5
6 2
6 3
6 6
7 2
7 3
7 7
Where the desired results should be :
id / id_category / tree
1 3 2
2 3 3
3 null null
4 null null
1 4 2
2 4 3
3 4 4
4 null null
.....
In words, I want to add a range id from 1 to 4 for each id_category, where if id_category has less than 4 value it should show null values.
Regards
To replicate the set of data you specified, I did this to get a working set:
CREATE TABLE cc (id_category INT, tree INT);
INSERT INTO cc VALUES (3,2),(3,3),(4,2),(4,3),(4,4),(5,2),(5,3),
(5,5),(6,2),(6,3),(6,6),(7,2),(7,3),(7,7);
SELECT cc.* FROM cc ORDER BY 1,2;
SQL Fiddle here: http://sqlfiddle.com/#!2/16249/3
Here's how I would approach the problem. First, I would get a distinct list of id_category values. That's straightforward. (I Could use a DISTINCT keyword rather than GROUP BY, whichever.)
SELECT id_category
FROM cc
GROUP BY id_category
ORDER BY id_category
Then, I would generate four rows from each of those rows. So, I'm going to wrap that previous query as an inline view (enclose it in a set of parenthesis, give it an alias, and reference the whole mess like it was just a tablename. Something like this:
SELECT c.id_category, j_.j
FROM (SELECT 1 AS j UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4) j_
JOIN (
SELECT cc.id_category
FROM cc cc
GROUP BY cc.id_category
ORDER BY cc.id_category
) c
ORDER BY c.id_category, j_.j
I'm using another inline view to return the integers 1 thru 4, and doing a CROSS JOIN to get four rows for each distinct id_category. That basically gets me the outline of the result set I want to return... but I don't have any values (other than NULL) for the tree column.
So now, I need to backup, and start working on another rowset, basically an ordered set from the cc table, but this time, including the value from the tree column. I'm not concerned here with getting exactly four rows, just the rows that have a value in the tree column. Again, very straightforward:
SELECT s.id_category_id, s.tree
FROM cc s
ORDER BY s.id_category, s.tree
But now, I want to assign each of those rows a relative row number, within each id_category. I can do by wrapping that query in a set of parenthesis, giving it an alias, and treating it like it were a table, like this:
SELECT #i := IF(r.id_category = #prev_idcat,#i + 1,1) AS i
, #prev_idcat := r.id_category AS id_category
, r.tree
FROM (SELECT #i := 0, #prev_idcat := NULL) i_
JOIN (
SELECT s.id_category, s.tree
FROM cc s
ORDER BY s.id_category, s.tree
) r
I'm using a MySQL trick with user variables, to assign ascending integer values, starting at 1, for each distinct id_category. The trick here is to have MySQL order the rows for me (in the inline view aliased as r, and "saving" the id_category from the previous row in a user variable, so I can compare it to the next row.
And now we're really getting to the point where having Common Table Expressions available in MySQL would be really, really nice. But since they aren't, we press forward, nesting our inline views.
So I'm going to give each of those "row numbering" queries an alias, and reference them like they were tables; The query is going to be of the form...
SELECT b.*, q.*
FROM ( ) b
LEFT
JOIN ( ) q
ON q.id_category = b.id_category AND q.i = b.j
(We omit the contents of those inline views just to get an overview of what the statement is really doing.)
This is going to start looking ugly, but this is where the magic happens.
I pull the four rows for each id_category from b, and I join that to q, matching on id_category and on "row number". It's a LEFT OUTER join, so I'm going to get all the rows from b, and pick up any "matching" row from q.
SELECT b.id_category, q.tree
FROM (SELECT c.id_category, j_.j
FROM (SELECT 1 AS j UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
) j_
JOIN (
SELECT cc.id_category
FROM cc cc
GROUP BY cc.id_category
ORDER BY cc.id_category
) c
ORDER BY c.id_category, j_.j
) b
LEFT
JOIN (SELECT #i := IF(r.id_category = #prev_idcat,#i + 1,1) AS i
, #prev_idcat := r.id_category AS id_category
, r.tree
FROM (SELECT #i := 0, #prev_idcat := NULL) i_
JOIN (
SELECT s.id_category, s.tree
FROM cc s
ORDER BY s.id_category, s.tree
) r
) q
ON q.id_category = b.id_category AND q.i = b.j
ORDER BY b.id_category, b.j
The only thing remaining in the specification is the generation of a value for an id column. If I was inserting to a table, I could use an AUTO_INCREMENT column to do it for me. But absent that, the most convenient place for me to generate an id value is in the inline view aliased as b. Just a little tweak, and finally, we have this monstrosity of a query, which returns the specified result set:
SELECT b.k AS id, b.id_category, q.tree
FROM (SELECT #k := #k + 1 AS k
, c.id_category
, j_.j
FROM (SELECT #k := 0) k_
JOIN (SELECT 1 AS j UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
) j_
JOIN (
SELECT cc.id_category
FROM cc cc
GROUP BY cc.id_category
ORDER BY cc.id_category
) c
ORDER BY c.id_category, j_.j
) b
LEFT
JOIN (SELECT #i := IF(r.id_category = #prev_idcat,#i + 1,1) AS i
, #prev_idcat := r.id_category AS id_category
, r.tree
FROM (SELECT #i := 0, #prev_idcat := NULL) i_
JOIN (
SELECT s.id_category, s.tree
FROM cc s
ORDER BY s.id_category, s.tree
) r
) q
ON q.id_category = b.id_category AND q.i = b.j
ORDER BY b.id_category, b.j
To get this to work with your rowset, you would replace each reference to my cc table with your query wrapped in parenthesis. Or, you could create a table named cc like I have done, and insert the results of your query into it.
Someone may have a simpler SQL statement that reliably produces the same result set. I'd be very interested to learn a simpler way.

Mysql query random order (pause and continue) question

Okay i am trying to create a mysql query that does this:
show 3 random records from table then after the 3th record show TEXT
and then show the same 3 items but other field (equaling to the items ofcourse) from same table.
eg table info:
--ids | titles------
10 | one
20 | two
30 | three
and the query results from the given example:
30 10 20 TEXT three one two
if anyone understand what i am asking,post your suggestion/asnwer
thanks for your time all :)
Just for kicks..
select t1.id, t2.id, t3.id, 'TEXT', t1.title, t2.title, t3.title
FROM
(
select #r := #r + 1 rownum, id
from (select #r:=0) initvar, (
select id
from tbl
order by rand()
limit 3
) X
) Y
join tbl t1 on Y.rownum=1 and t1.id = Y.id
join tbl t2 on Y.rownum=2 and t2.id = Y.id
join tbl t3 on Y.rownum=3 and t3.id = Y.id
You should really just do the query below, and do whatever display processing using the 3 rows returned, in whatever programming environment you use (Java/PHP/.Net etc).
select id, title
from tbl
order by rand()
limit 3
EDIT
To get the data in 7 different rows, you can use the below. I stress again that this is front-end display code. I will not use such SQL code in a production system.
select display
from
(
select sorter, rownum,
case when sorter=3 then title else id end display
from
(
select #r := #r + 1 rownum, id, title
from (select #r:=0) initvar,
(
select id, title
from tbl
order by rand()
limit 3
) X
) Y, (select 1 sorter union all select 3) dup
union all
select 2, 0, 'TEXT'
) Z
order by sorter, rownum
Example Output
7
2
1
TEXT
test 7 << title for id=7
test 2
test 1