So, this is the challenge:
I have two tables:
Etalon:
+-----+-----+-----+-----+----+
| e1 | e2 | e3 | e4 | e5 |
+-----+-----+-----+-----+----+
| 01 | 02 | 03 | 04 | 05 |
+-----+-----+-----+-----+----+
And Candidates:
+-----+----+-----+-----+-----+----+----+
| ID | c1 | c2 | c3 | c4 | c5 | nn |
+-----+----+-----+-----+-----+----+----+
| 00 | 03 | 08 | 02 | 01 | 06 | ** |
+-----+----+-----+-----+-----+----+----+
| 01 | 05 | 04 | 03 | 02 | 01 | ** |
+-----+----+-----+-----+-----+----+----+
| 02 | 06 | 07 | 08 | 09 | 10 | ** |
+-----+----+-----+-----+-----+----+----+
| 03 | 08 | 06 | 09 | 02 | 07 | ** |
+-----+----+-----+-----+-----+----+----+
What request should I use, to find and save (in nn column) the number of matches between two rows (e1, e2, e3, e4, e5 and c1, c2, c3, c4, c5) for each row in table candidate?
Should be the next result:
Candidates:
|-----|----|-----|-----|-----|-----|----|
| ID | c1 | c2 | c3 | c4 | c5 | nn |
|-----|----|-----|-----|-----|-----|----|
| 00 | 03 | 08 | 02 | 01 | 06 | 03 |
|-----|----|-----|-----|-----|-----|----|
| 01 | 05 | 04 | 03 | 02 | 01 | 05 |
|-----|----|-----|-----|-----|-----|----|
| 02 | 06 | 07 | 08 | 09 | 10 | 00 |
|-----|----|-----|-----|-----|-----|----|
| 03 | 08 | 06 | 09 | 02 | 07 | 01 |
|-----|----|-----|-----|-----|-----|----|
The result for nn is:
0 - no matches
1,2,3,4,5 - numbers of matches
How can I achieve that?
The objective is to establish a maximal partial matching between the master row and each row of the client table without regard to the respective column identities.
The idea is to abstract away from the column ids by representing the column contents in another way. As you indicated that the value domain is {1, ..., 10}, one may choose the first 10 prime numbers {p_1, ...,p_10} = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 }, mapping i to p_i. The comparisons will be based on the product of the mapped column values. This approach exploits the uniqueness of prime factorization, ie. every positive integer factorizes into a unique multi-set of prime numbers.
A one-pass standalone sql update statement is rather cumbersome to write down, therefore we create a temporary table that contains the products of the mapped values:
CREATE TEMPORARY TABLE t_pp (
id NUMBER
, mp_candidates NUMBER
, mp_etalon NUMBER
, nn NUMBER
);
INSERT INTO t_pp ( id, mp_candidates, mp_etalon )
SELECT id
, CASE c1
WHEN 1 THEN 2
WHEN 2 THEN 3
WHEN 3 THEN 5
WHEN 4 THEN 7
WHEN 5 THEN 11
WHEN 6 THEN 13
WHEN 7 THEN 17
WHEN 8 THEN 19
WHEN 9 THEN 23
WHEN 10 THEN 29
ELSE 31
END
* CASE c2 WHEN 2 THEN 3 WHEN 3 THEN 5 WHEN 4 THEN 7 WHEN 5 THEN 11 WHEN 6 THEN 13 WHEN 7 THEN 17 WHEN 8 THEN 19 WHEN 9 THEN 23 WHEN 10 THEN 29 ELSE 31 END
* CASE c3 WHEN 2 THEN 3 WHEN 3 THEN 5 WHEN 4 THEN 7 WHEN 5 THEN 11 WHEN 6 THEN 13 WHEN 7 THEN 17 WHEN 8 THEN 19 WHEN 9 THEN 23 WHEN 10 THEN 29 ELSE 31 END
* CASE c4 WHEN 2 THEN 3 WHEN 3 THEN 5 WHEN 4 THEN 7 WHEN 5 THEN 11 WHEN 6 THEN 13 WHEN 7 THEN 17 WHEN 8 THEN 19 WHEN 9 THEN 23 WHEN 10 THEN 29 ELSE 31 END
* CASE c5 WHEN 2 THEN 3 WHEN 3 THEN 5 WHEN 4 THEN 7 WHEN 5 THEN 11 WHEN 6 THEN 13 WHEN 7 THEN 17 WHEN 8 THEN 19 WHEN 9 THEN 23 WHEN 10 THEN 29 ELSE 31 END
mp_candidates
, CASE e1
WHEN 1 THEN 2
WHEN 2 THEN 3
WHEN 3 THEN 5
WHEN 4 THEN 7
WHEN 5 THEN 11
WHEN 6 THEN 13
WHEN 7 THEN 17
WHEN 8 THEN 19
WHEN 9 THEN 23
WHEN 10 THEN 29
ELSE 31
END
* CASE e2 WHEN 2 THEN 3 WHEN 3 THEN 5 WHEN 4 THEN 7 WHEN 5 THEN 11 WHEN 6 THEN 13 WHEN 7 THEN 17 WHEN 8 THEN 19 WHEN 9 THEN 23 WHEN 10 THEN 29 ELSE 31 END
* CASE e3 WHEN 2 THEN 3 WHEN 3 THEN 5 WHEN 4 THEN 7 WHEN 5 THEN 11 WHEN 6 THEN 13 WHEN 7 THEN 17 WHEN 8 THEN 19 WHEN 9 THEN 23 WHEN 10 THEN 29 ELSE 31 END
* CASE e4 WHEN 2 THEN 3 WHEN 3 THEN 5 WHEN 4 THEN 7 WHEN 5 THEN 11 WHEN 6 THEN 13 WHEN 7 THEN 17 WHEN 8 THEN 19 WHEN 9 THEN 23 WHEN 10 THEN 29 ELSE 31 END
* CASE e5 WHEN 2 THEN 3 WHEN 3 THEN 5 WHEN 4 THEN 7 WHEN 5 THEN 11 WHEN 6 THEN 13 WHEN 7 THEN 17 WHEN 8 THEN 19 WHEN 9 THEN 23 WHEN 10 THEN 29 ELSE 31 END
mp_etalon
, 0 nn
FROM candidates
CROSS JOIN etalon
;
Now for pass #2 - counting matches:
UPDATE t_pp
SET nn =
CASE WHEN mp_candidates MOD 2 = 0 AND mp_etalon MOD 2 = 0 THEN 1 ELSE 0 END
+ CASE WHEN mp_candidates MOD 3 = 0 AND mp_etalon MOD 3 = 0 THEN 1 ELSE 0 END
+ CASE WHEN mp_candidates MOD 5 = 0 AND mp_etalon MOD 5 = 0 THEN 1 ELSE 0 END
+ CASE WHEN mp_candidates MOD 7 = 0 AND mp_etalon MOD 7 = 0 THEN 1 ELSE 0 END
+ CASE WHEN mp_candidates MOD 11 = 0 AND mp_etalon MOD 11 = 0 THEN 1 ELSE 0 END
+ CASE WHEN mp_candidates MOD 13 = 0 AND mp_etalon MOD 13 = 0 THEN 1 ELSE 0 END
+ CASE WHEN mp_candidates MOD 17 = 0 AND mp_etalon MOD 17 = 0 THEN 1 ELSE 0 END
+ CASE WHEN mp_candidates MOD 19 = 0 AND mp_etalon MOD 19 = 0 THEN 1 ELSE 0 END
+ CASE WHEN mp_candidates MOD 23 = 0 AND mp_etalon MOD 23 = 0 THEN 1 ELSE 0 END
+ CASE WHEN mp_candidates MOD 29 = 0 AND mp_etalon MOD 29 = 0 THEN 1 ELSE 0 END
;
Finally, transferring the results to the original table and cleaning up:
UPDATE candidates c
set nn = ( SELECT p.nn FROM t_pp p WHERE p.id = c.id )
;
DELETE TEMPORARY TABLE t_pp;
Some more notes:
The scheme as shown assumes that cell values are unique within each row. However, it can easily be extended to allow formultiple occurrences of values.
In principle, this can be wrapped in a single sql statement - for obvious reasons this is not recommended.
Rdbms other than mysql follow the sql standard and provide the WITH clause that obviates the need for a temporaray table.
The value 31 in the ELSE branch of the above CASE expressions is a dummy value.
Related
This question already has answers here:
How can I return pivot table output in MySQL?
(10 answers)
Closed 2 years ago.
My table look like this
name value monthyear year
g1 10 March 18 2018
g1 11 March 18 2018
g1 34 March 19 2019
g1 45 March 19 2019
g2 10 April 18 2018
g1 11 May 18 2018
g1 34 May 19 2019
g1 45 June 19 2019
And I need out put like this
Name March 18 March 19 April 18 May 18 May 19 June 19
g1 21 79 11 34 45
g2 10
In Pivot table I am getting this, How can I get this table with sql query ? Please help & Thanks in advance.
You can use case expression inside sum. here is the demo.
select
name,
sum(case when monthyear = 'March 18' then value end) as March18
sum(case when monthyear = 'March 19' then value end) as March19
..
..
from yourTable
group by
name
output:
| name | March_18 | March_19 | April_18 | May_18 | May_19 | June_19 |
| ---- | -------- | -------- | -------- | ------ | ------ | ------- |
| g1 | 21 | 79 | | 11 | 34 | |
| g2 | | | 10 | | | |
I'm trying to display zero on missing month, but didn't succeed.
Table:
clicks | impressions | ctr | position | month | year
111 2709 4 20 3 2015
101 2695 3 20 6 2015
76 2714 2 21 7 2015
.
.
.
64 1212 4 25 11 2015
81 1905 4 24 12 2015
Required output:
clicks | impressions | ctr | position | month | year
0 0 0 0 1 2015
0 0 0 0 2 2015
111 2709 4 20 3 2015
0 0 0 0 4 2015
0 0 0 0 5 2015
101 2695 3 20 6 2015
.
.
.
64 1212 4 25 11 2015
81 1905 4 24 12 2015
Like #jarlh suggested ,you can do it like this: creating a 'table' that contains all month availabe(you will have to populate it your self, add which month and years you want) and then left join to the original table and when value not exists, put 0.
select coalese(s.clicks,0) as clicks,
coalese(s.impressions ,0) as impressions,
coalese(s.ctr ,0) as ctr ,
coalese(s.position ,0) as position ,
t.month,
t.year
from(
SELECT 1 as month_num,2015 as year_num
union SELECT 2,2015
union select 3,2015
union select 4,2015 ....) t
LEFT OUTER JOIN YourTable s
ON(t.month_num = s.month and t.year_num = s.year)
In a re-print of a deleted question an hour ago,
if I wanted to print out the numbers 1-100, with 10 numbers to a line
in the mysql shell, how would I go about doing that?
Community wiki answer so as not to collect points. Edit at will.
select theAnswer
from
( select #rn:=#rn+1 as rownum,
concat(1+(#rn-1)*10,' ',2+(#rn-1)*10,' ',3+(#rn-1)*10,' ',4+(#rn-1)*10,' ',5+(#rn-1)*10,' ',
6+(#rn-1)*10,' ',7+(#rn-1)*10,' ',8+(#rn-1)*10,' ',9+(#rn-1)*10,' ',10+(#rn-1)*10,' ') as theAnswer
from (select #rn:=0) params1
cross join (select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9 union select 10) params2
) xDerived;
+---------------------------------+
| theAnswer |
+---------------------------------+
| 1 2 3 4 5 6 7 8 9 10 |
| 11 12 13 14 15 16 17 18 19 20 |
| 21 22 23 24 25 26 27 28 29 30 |
| 31 32 33 34 35 36 37 38 39 40 |
| 41 42 43 44 45 46 47 48 49 50 |
| 51 52 53 54 55 56 57 58 59 60 |
| 61 62 63 64 65 66 67 68 69 70 |
| 71 72 73 74 75 76 77 78 79 80 |
| 81 82 83 84 85 86 87 88 89 90 |
| 91 92 93 94 95 96 97 98 99 100 |
+---------------------------------+
The stuff inside of the from ( ) is a derived table, and every derived table needs an alias, which is xDerived.
#rn is a row number variable. It gets initialized in the params1 derived table. One row.
params2 is another derived table, with rows 1 to 10 as values.
The cross join creates a cartesian product (all permutations) of a 1x10 which results in 10 rows, with #rn getting incremented with each row.
As we only want one column of output, the outer wrapper does the final select for just one column to avoid outputting the row number column.
If one wanted to use a WHILE DO loop in mysql, one could use a stored procedure.
Generally what i do is create a table (normally a temp table) and populate that with a stored procedure.
CREATE TABLE `numTable` (
`Id` int(11) NOT NULL auto_increment,
PRIMARY KEY (`Id`)
)//
CREATE PROCEDURE dowhile(IN tableLimit INT)
BEGIN
DECLARE pointer INT DEFAULT tableLimit;
WHILE pointer > 0 DO
INSERT numTable VALUES (NULL);
SET pointer = pointer - 1;
END WHILE;
END//
CALL dowhile(100)//
now you may need to use DELIMITER but for the sake of consistency i have just copied what worked in SQL Fiddle by setting the Schema Delimiter to be // (forth button bellow the Schema Window)
then from there i then do a select of this table by giving each row a group id. since you want groups of 10 i have set the group to be multiples of 10 and then group by this group id using GROUP_CONCAT to make the rows.
select myRow
from (
SELECT group_concat(id SEPARATOR ', ') as `myRow`, CEIL(id/10) as `groupId`
FROM numTable group by `groupID`) as myTable;
SQL Fiddle
since we don't want to show the group id i then make this a sub-select and only select my new rows. if you use this in something like PHP or C# to output the rows you can just do the one select since you don't have to output everything you get from a query result.
In MariaDB with the sequence plugin:
select group_concat(seq order by seq separator ' ')
from seq_1_to_100
group by (seq-1) div 10;
| group_concat(seq order by seq separator ' ') |
|----------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 |
| 11 12 13 14 15 16 17 18 19 20 |
| 21 22 23 24 25 26 27 28 29 30 |
| 31 32 33 34 35 36 37 38 39 40 |
| 41 42 43 44 45 46 47 48 49 50 |
| 51 52 53 54 55 56 57 58 59 60 |
| 61 62 63 64 65 66 67 68 69 70 |
| 71 72 73 74 75 76 77 78 79 80 |
| 81 82 83 84 85 86 87 88 89 90 |
| 91 92 93 94 95 96 97 98 99 100 |
A generic solution:
set #num_cols := 10;
set #max := 100;
select group_concat(seq order by seq separator ' ')
from seq_1_to_1000000
where seq <= #max
group by (seq-1) div #num_cols
order by min(seq);
If you want them all in one cell:
select group_concat(col separator '\n')
from (
select group_concat(seq order by seq separator '\t') as col
from seq_1_to_1000000
where seq <= #max
group by (seq-1) div #num_cols
) drv
Want to have columns?
set #num_cols := 7;
set #num_rows := 3;
set #sql := (
concat('select ', (
select group_concat('(seq-1)*', #num_cols, '+', seq, ' as c', seq)
from seq_1_to_1000000
where seq <= #num_cols
),' from seq_1_to_1000000 where seq<=', #num_rows)
);
prepare stmt from #sql;
execute stmt;
| c1 | c2 | c3 | c4 | c5 | c6 | c7 |
|----|----|----|----|----|----|----|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
If you don't have MariaDB with the sequence plugin, you can create a helper table with sequence numbers. Ask Drew how to do that :-)
I have a table XX. i need to get the records which are 20 days consecutive gap .below is my table look
ID ISmen Date
1 0 2013-05-2
2 0 2013-05-2
3 0 2014-04-2
4 1 2014-05-2
5 1 2014-05-2
6 0 2014-05-2
7 0 2014-05-2
8 0 2014-05-2
9 1 2014-05-25
10 1 2014-05-25
11 0 2014-05-26
12 1 2014-05-27
13 0 2014-05-28
From the above table i need to get the records which are ismen is 1 and the next record ismen is also 1 (i.e 4,5 and 9,10 but not 12).and one more thing 4,5 and 9,10 should have 20 days gap
i am getting the records which are 4,5 and 9,10 ..but i can't able to check date difference between the records .i know we can achieve in the loop but i am trying to get in MySQL is it possible or not.I try below query.thanks in advance for help
SELECT *
FROM XX t1,
XX t2
WHERE (t1.ID=t2.ID+1
OR t1.ID=t2.ID-1)
AND t1.Ismen=1
AND t2.Ismen=1
There is a 23 day gap between |4|5| to |9|10| but ignoring the sample data precision, this result:
| ISMEN | T1ID | T2ID | T1DATE | T2DATE |
|-------|------|------|----------------------------|----------------------------|
| 1 | 4 | 9 | May, 02 2014 00:00:00+0000 | May, 25 2014 00:00:00+0000 |
| 1 | 5 | 9 | May, 02 2014 00:00:00+0000 | May, 25 2014 00:00:00+0000 |
| 1 | 4 | 10 | May, 02 2014 00:00:00+0000 | May, 25 2014 00:00:00+0000 |
| 1 | 5 | 10 | May, 02 2014 00:00:00+0000 | May, 25 2014 00:00:00+0000 |
was produced by this query:
select
t1.ismen
, t1.id as t1id
, t2.id as t2id
, t1.`date` as t1date
, t2.`date` as t2date
from table1 as t1
inner join table1 as t2 on t1.ismen = t2.ismen
and t1.`date` + INTERVAL 23 DAY = t2.`date`
The wanted gap between records can be defined in the join conditions (change to 20 or whatever). But do note there is nothing to stop 4 relating to 9 and 10 or 5 to 9 & 10 so you get 4 records in total.
see: http://sqlfiddle.com/#!9/8d941/1
You could reduce that result by some means (e.g. using row_number() but I don't know if that is required.
I have a query
SELECT ckt, setpt, clock FROM progs
WHERE feed = "80302" AND day=4 AND clock<"12:15:00"
ORDER BY ckt, clock DESC
That gets me this:
ckt setpt clock
0 69 06:06:00
0 62 00:30:00
1 57 10:30:00
1 67 04:30:00
1 57 01:30:00
2 69 11:00:00
2 62 00:30:00
How could I modify this query to give me the MAX(clock) for each CKT
In total:
Each cktand day has a number of setpt and clock entries. I am looking for the (1 or none) clock record < some_time for each ckt and day. ie
0 69 06:06:00
1 67 10:30:00
2 69 11:00:00
in mysql.
Something like this?
SELECT ckt, MAX(setpt), MAX(clock)
FROM progs
WHERE feed = "80302" AND day=4 AND clock<"12:15:00"
GROUP BY ckt
ORDER BY ckt, clock DESC
Result
| CKT | MAX(SETPT) | MAX(CLOCK) |
-----------------------------------------------------
| 0 | 69 | January, 01 1970 06:06:00+0000 |
| 1 | 67 | January, 01 1970 10:30:00+0000 |
| 2 | 69 | January, 01 1970 11:00:00+0000 |
See the demo
Sounds like you need to JOIN the table to itself, joining on the MAX(clock):
SELECT p.ckt, p.setpt, p.clock
FROM progs p
JOIN (
SELECT MAX(clock) maxClock, ckt
FROM progs
WHERE feed = "80302"
AND day=4
AND clock<"12:15:00"
GROUP BY ckt
) p2 on p.ckt = p2.ckt AND p.clock = p2.maxclock
ORDER BY p.ckt, p.clock DESC
SQL Fiddle Demo (borrowed from other post)
Producing:
0 69 January, 01 1970 06:06:00+0000
1 57 January, 01 1970 10:30:00+0000
2 69 January, 01 1970 11:00:00+0000
Please note row with id 1 is different than your desired results, but match your question's desired results...