I am creating a simple PHP webpage with MySQL database.
The MYSQL database have the following contents: (illustration on left)
I just want to know a possible way of doing some Data Manipulation to achieve the results on the right portion below.
(initial value) (final result)
ID | MINUS '1'(to "next" ID's) ID | ID |
1 | 0 1 | ------------> 1 |
2 | 1 --> (will minus '1' to ID 3 2 | ------------> 2 |
3 | 0 up to the last) 3-1 = 2 | ------------> 2 |
4 | 0 4-1 = 3 | ------------> 3 |
5 | 1 --> (minus '1' AGAIN to ID 6 5-1 = 4 | ------------> 4 |
6 | 0 up to the last) 6-1 = 5 -1 ---> 4 |
7 | 0 7-1 = 6 -1 ---> 5 |
It is just COUNTING the number of minus and then subtracting it to ALL the ID that follows it. ID 2 has tells the next ID, which is 3 to subtract 1 to itself up to the last ID, (3-1 , 4-1, 5-1, 6-1, 7-1 ) but another instance came. ID 5 tells ID 6 to subtract itself again up to the last, so MINUS 2 from ID 6 up to the last ID. from just 6-1, 7-1 ---> 6-2, 7-2.
I know it sounds very easy for you, but I'm just a newbie and find this thing hard. Sorry for the headache. Hope someone helps, Thanks!
my DML is like (just for illustration, this should be in php, I will convert it, just help me in the LOGIC)
for($x=0;$x<num_rows;$x++)
{
if(MINUS = 1){
query(UPDATE table_name SET ID=ID-1 WHERE ID = $x);}
}
Something like that, i am still a newbie in decision making loops. I will be grateful if you could fix my loop. I am new to this community, I beg for your understanding.
You can do this in MySQL either using variables or standard SQL. Here is the standard SQL:
select id, minus,
(id - coalesce((select sum(minus) from table t2 where t2.id < t.id), 0)) as newId
from table t;
Here is the variable version:
select id, minus,
(id - minus + (#value := #value + minus)) as newId
from table t cross join
(select #value = 0) const
order by id
You can use a MySQL implementation as follows:
select id, minus,
(id - coalesce((select sum(minus) from table t2 where t2.id < t.id), 0)) as newId
from table t;
The code idea came from Gordon's post, here is an elaboration.
The first part is select which states that you want to select certain input from mysql. id, minus follows, and means that you want to return the values of id and minus when the query has finished running.
The rest of it falls under the third part, where you perform the subtraction, and return the value of the subtraction as newId. So when the sql returns, it will get a row with ID, MINUS and NEWID as it's headings.
The subtraction works like this: coalesce returns the first non-null value (i.e. 1) in the list. the table t2 is using a second version of the table and calling it t2 so that you can compare information.
I hope this clarified things for you!
Related
My site allows users to guess the result of a sports match. At the end of the match the guesses should be compared to the actual result. The winner(s) are the members with the closest correct guess
Im looking for a way to return all members who guessed the correct result and score difference IF NO (zero) member guessed correctly return members who guessed closest to the correct result
See MYSQL FIDLE EXAMPLE
I modified the script to change fixed values taking variables as you can see below
if(isset($_POST['resultBtn'])){
foreach($_POST['winner'] as $id =>$winner){
$winScore = $_POST['score'][$id];
:
:
$sql="SELECT p.*
FROM Multiple_Picks p
WHERE p.event_id='$matchId' AND
p.pick='$winner' AND
abs(p.score-'$winScore') = (SELECT min(abs(p2.score-1))
FROM Multiple_Picks p2
Where p2.pick=p.pick AND
p2.event_id = p.event_id)";
My problem is if I run this script on the following table:
NOTHING gets displayed even if I put result exactly correct:
My variable values are correct in the sql statment so that is not the problem
Any help will be welcomed...
IMPORTANT THE USER WHO SELECTED CLOSEST CORRECT RESULTS, FOR ALL GAME, DURING THE ROUND IS THE WINNER
example: if user A won 4 of the picks and user B won 5 of the picks then user B is the winner of the round
Why don't you want just
SELECT p.*, abs(p.score-'$winScore') as diff
FROM Multiple_Picks p
WHERE p.event_id='$matchId' AND p.pick='$winner'
ORDER BY diff ASC
LIMIT 1
This will return the closest member for the event. Remove the LIMIT if you need a few of them.
Also, never put your parameters directly into the SQL query, even trusted ones (not your case) and even if you're sure they will always be integer or non-string type. Use prepared statements.
In this answer I call a "Best" pick any pick that has chosen the correct winner for a particular match, and has the closest score to the actual match score.
These scripts also respect the different "rounds" in the competition, since that is an important complication.
This answer comes in two parts: first a query that is similar to the one in the question that returns all the "Best" picks for a particular match. To make it easier to run in SQL Fiddle, I have used MySQL variables instead of PHP variables.
Schema with test data:
create table Multiple_Picks (
pick_id int,
member_nr int,
event_id int,
pick varchar(100),
score int
);
insert into Multiple_Picks
values
(11,100,1,'Crusaders',15),
(12,100,2,'Waratahs',10),
(13,100,3,'Chiefs',4),
(21,200,1,'Crusaders',15),
(22,200,2,'Waratahs',10),
(23,200,3,'Lions',4),
(31,300,1,'Crusaders',15),
(32,300,2,'Waratahs',12),
(33,300,3,'Lions',6),
(41,100,4,'Crusaders',20),
(42,100,5,'Waratahs',20),
(43,100,6,'Lions',20)
;
Queries to show all picks and then best picks for a particular match:
set #matchId = 2;
set #winner = 'Waratahs';
set #winScore = 8;
-- Show all picks for a particular match
select * from Multiple_Picks
where event_id = #matchId;
-- Show best picks for a particular match
select p.*
from Multiple_Picks p
where p.event_id = #matchId
and p.pick = #winner
and abs(p.score - #winScore) =
(select min(abs(other.score - #winScore))
from Multiple_Picks other
where other.event_id = #matchId
and other.pick = #winner
)
;
SQL Fiddle to show picks for particular match
-- Show all picks for a particular match
+---------+-----------+----------+----------+-------+
| pick_id | member_nr | event_id | pick | score |
+---------+-----------+----------+----------+-------+
| 12 | 100 | 2 | Waratahs | 10 |
| 22 | 200 | 2 | Waratahs | 10 |
| 32 | 300 | 2 | Waratahs | 12 |
+---------+-----------+----------+----------+-------+
-- Show best picks for a particular match
+---------+-----------+----------+----------+-------+
| pick_id | member_nr | event_id | pick | score |
+---------+-----------+----------+----------+-------+
| 12 | 100 | 2 | Waratahs | 10 |
| 22 | 200 | 2 | Waratahs | 10 |
+---------+-----------+----------+----------+-------+
Now we need to work towards finding the winner of each round of the competition.
First we have extra test data that contains the actual scores for Matches in rounds 1 and 2.
create table Matches (
event_id int,
winner varchar(100),
score int,
round int
);
insert into Matches
values
(1,'Crusaders',10,1),
(2,'Waratahs',11,1),
(3,'Lions',4,1),
(4,'Crusaders',20,2),
(5,'Waratahs',20,2),
(6,'Chiefs',20,2)
;
Now select the best picks for all Matches. The subselect (aliased as m) calculates best_diff for each match as the minimum difference between the actual score and every guessed score. This subselect is then joined to every pick so that only "Best" picks are returned.
-- Show all best picks for all Matches
select p.*, m.round
from Multiple_Picks p
join (
select m2.event_id, m2.winner, m2.score, m2.round,
min(abs(m2.score-p2.score)) as best_diff
from Matches m2
join Multiple_Picks p2
on p2.event_id = m2.event_id and p2.pick = m2.winner
group by m2.event_id, m2.winner, m2.score, m2.round
) as m
on p.event_id = m.event_id and p.pick = m.winner
and abs(m.score - p.score) = m.best_diff
order by m.round, p.event_id
;
It is then easy to get a count of Best picks for each player for each round by just grouping the previous query by member_nr and round:
-- Show a count of best picks for each player for each round
select p.member_nr, m.round, count(*) as best_count
from Multiple_Picks p
join (
select m2.event_id, m2.winner, m2.score, m2.round,
min(abs(m2.score-p2.score)) as best_diff
from Matches m2
join Multiple_Picks p2
on p2.event_id = m2.event_id and p2.pick = m2.winner
group by m2.event_id, m2.winner, m2.score, m2.round
) as m
on p.event_id = m.event_id and p.pick = m.winner
and abs(m.score - p.score) = m.best_diff
group by p.member_nr, m.round
order by m.round, count(*) desc
;
SQL Fiddle for all best picks and counts for all matches
-- Show all best picks for all Matches
+---------+-----------+----------+-----------+-------+-------+
| pick_id | member_nr | event_id | pick | score | round |
+---------+-----------+----------+-----------+-------+-------+
| 31 | 300 | 1 | Crusaders | 15 | 1 |
| 21 | 200 | 1 | Crusaders | 15 | 1 |
| 11 | 100 | 1 | Crusaders | 15 | 1 |
| 12 | 100 | 2 | Waratahs | 10 | 1 |
| 32 | 300 | 2 | Waratahs | 12 | 1 |
| 22 | 200 | 2 | Waratahs | 10 | 1 |
| 23 | 200 | 3 | Lions | 4 | 1 |
| 41 | 100 | 4 | Crusaders | 20 | 2 |
| 42 | 100 | 5 | Waratahs | 20 | 2 |
+---------+-----------+----------+-----------+-------+-------+
-- Show a count of best picks for each player for each round
+-----------+-------+------------+
| member_nr | round | best_count |
+-----------+-------+------------+
| 200 | 1 | 3 |
| 300 | 1 | 2 |
| 100 | 1 | 2 |
| 100 | 2 | 2 |
+-----------+-------+------------+
The final stage is to select only those players for each round who have the highest number of Best picks. I tried modifying the above queries, but the nesting becomes two confusing, so my solution was to create a few logical views so that the final query can be more easily understood. The views basically encapsulate the logic of the queries I have explained above:
create view MatchesWithBestDiff as
select m.event_id, m.winner, m.score, m.round,
min(abs(m.score-p.score)) as best_diff
from Matches m
join Multiple_Picks p
on p.event_id = m.event_id and p.pick = m.winner
group by m.event_id, m.winner, m.score, m.round
;
create view BestPicks as
select p.*, m.round
from Multiple_Picks p
join MatchesWithBestDiff m
on p.event_id = m.event_id and p.pick = m.winner
and abs(m.score - p.score) = m.best_diff
;
create view BestPickCount as
select member_nr, round, count(*) as best_count
from BestPicks
group by member_nr, round
;
So that the query that shows the winners of each round is simply:
-- Show the players with the highest number of Best Picks for each round
select *
from BestPickCount p
where best_count =
(
select max(other.best_count)
from BestPickCount other
where other.round = p.round
)
order by round
;
SQL Fiddle for players with most Best picks for each round
-- Show the players with the highest number of Best Picks for each round
+-----------+-------+------------+
| member_nr | round | best_count |
+-----------+-------+------------+
| 200 | 1 | 3 |
| 100 | 2 | 2 |
+-----------+-------+------------+
This whole investigation has reminded me how tricky it can be to get SQL to do much manipulation where records need to be selected depending on maximums and sums. Some of these types of queries can be much easier with window functions (the OVER and PARTITION BY clauses), but they are not available in MySQL.
While designing the above queries, I found a few interesting restrictions:
MySQL does not allow joins to subqueries in views definitions.
ANSI SQL does not allow an aggregate in a subquery to reference both a column from the inner query and a column from the outer query. MySQL seems to sometimes allow this, but I couldn't find clear guidance as to when it is allowed, so I chose to code the above queries to avoid this "feature".
scenario 1: NO USERS SELECTED THE CORRECT TEAM
I believe that result in this situation should be empty result because everyone has made a mistake.
SCORE RETURN MEMBERS WHO SELECTED THE CLOSEST TO CORRECT SCORE AND
RESULT
It seems to be already working in your code example except one mistake in select.
abs(p.score-'$winScore') = (SELECT min(abs(p2.score-1))
Instead of constant 1 (one) it should be variable '$winScore'
and to control the number of users you get, you may limit your results so you will get something like this:
$sql="SELECT p.*
FROM Multiple_Picks p
WHERE p.event_id='$matchId' AND
p.pick='$winner' AND
abs(p.score-'$winScore') = (SELECT min(abs(p2.score-'$winner'))
FROM Multiple_Picks p2
Where p2.pick=p.pick AND
p2.event_id = p.event_id)
order by p.id limit '$numberOfMembers'";
SCENARIO 2: SCENARIO 2: MULTIPLE USERS SELECTED CORRECT TEAM BUT
SCORES ARE DIFFERENT RETURN USER(S) WHO GUESSED CLOSEST TO CORRECT
SCORE
Same as in the previous question.
SCENARIO 3: MULTIPLE USERS SELECTED CORRECT TEAM AND SCORE RETURN ALL
USERS WHO SELECTED CORRECT TEAM AND SCORE
You can achieve this using same query just replace the LIMIT with 'rank' function, and also if you will get several closest scores, but you have to limit their number according to their voting order by id, for this purpose I suggest sorting.
So final query will be:
$sql="select * from (SELECT p.*,
abs(p.score-'$winScore') scr_diff,
#rownum := #rownum + 1 rank
FROM Multiple_Picks p,
(SELECT #rownum := 0) rank_gen
WHERE p.event_id='$matchId' AND
p.pick='$winner' AND
abs(p.score-'$winScore') = (SELECT min(abs(p2.score-'$winner'))
FROM Multiple_Picks p2
Where p2.pick=p.pick AND
p2.event_id = p.event_id)
order by p.id
) sq
where sq.scr_diff = 0
or sq.rank < '$numberOfMembers'";
Fiddle.
Best guesser for one match
First find the member(s) who picked the winner and had the closest score guess:
SELECT p.*
FROM
( SELECT MIN(ABS(score-'$winScore')) AS closest
FROM Multiple_Picks
WHERE event_id = '$matchId'
AND pick='$winner'
) AS c
JOIN Multiple_Picks p
WHERE p.event_id = '$matchId'
AND p.pick = '$winner'
AND ABS(score-'$winScore') = c.closest
If that return no results, then what should happen? (It would be because no one picked the winner for a particular event.)
But, I think your question is much more complex. However, the above gives a mapping from (event_id, pick) -> list-of-members who "won". Starting over...
Missing info
There is a mystery -- Where do the event results come from? I will assume this table is already populated:
CREATE TABLE Win (
event_id ..., -- which game
winnner ..., -- who won
score ... -- by what score
)
Best guesser overall
So, create a table of BestGuessers(event_id, member). The details of "all game" and "round" are a bit vague. So I will carry this at least one step further.
CREATE TEMPORARY TABLE BestGuessers(
event_id ...,
member_nr ... -- who guessed the best for that event
)
SELECT p.event_id, p.member_nr
FROM
( SELECT w.event_id, w.winner, MIN(ABS(mp.score-w.score)) AS closest
FROM Multiple_Picks AS mp
JOIN Win AS w ON mp.event_id = w.event_id
AND mp.pick = w.winner
GROUP BY w.event_id, w.winner
) AS c
JOIN Multiple_Picks p
ON p.event_id = c.event_id
AND p.pick = c.pick
AND p.score = c.closest
Now, from that, you can pick the best guesser(s).
SELECT y.member_nr
FROM
( SELECT COUNT(*) AS ct
FROM BestGuessers
GROUP BY member_nr
ORDER BY COUNT(*) DESC
LIMIT 1
) AS x -- the max number of correct guesses
STRAIGHT_JOIN
( SELECT member_nr, COUNT(*) AS ct
FROM BestGuessers
GROUP BY member_nr
) AS y -- the users who guessed correctly that many times
USING (ct);
All this is pretty complex; I may have some typos, even logic errors. But maybe I came close.
It seems an additional table to store the actual results would help here.
E.g let's say this is in a table called results with sample values as follows:
event_id winner result
1 Crusaders 16
2 Waratahs 15
3 Chiefs 4
4 Crusaders 17
5 Reds 12
0 Rebels 14
7 Cheetahs 15
8 Crusaders 14
This can then be JOINed on each row and results compared as follows:
SELECT p.*
, CASE WHEN ABS(p.score - r.result)
- CASE WHEN p.pick = r.winner THEN 999999 ELSE 0 END
= (SELECT MIN(ABS(p2.score - r2.result)
- CASE WHEN p2.pick = r2.winner THEN 999999 ELSE 0 END)
FROM picks p2
JOIN results r2
ON p2.event_id = r2.event_id
WHERE p2.event_id = p.event_id)
THEN 1
ELSE 0
END AS win
FROM picks p
JOIN results r
ON p.event_id = r.event_id;
Explanation
The rightmost win column is 1 if the member is calculated to have won or drawn the event, otherwise it is 0. The method used is similar to the one in your post, with the main difference being the team and score are combined. The main thing to be explained here is the 999999, which is subtracted when a correct team is picked - so this can be sure to eclipse the score difference. (Of course, an even bigger value could be picked if needed).
Demo
SQL Fiddle Demo
I have a table that holds price information. I need to select the max value of every three rows. EXAMPLE:
Table `daily_high`
____ _______
| ID | HIGH |
| 1 | 24.65 |
| 2 | 24.93 |
| 3 | 26.02 |
| 4 | 25.33 |
| 5 | 25.16 |
| 6 | 25.91 |
| 7 | 26.05 |
| 8 | 28.13 |
| 9 | 27.07 |
|____|_______|
Desired output to new table (ID will be auto-increment so don't assume an association exists between this ID 1 and the daily_high ID 1:
____ ___________
| ID | 3MaxHIGH |
|____|___________|
| 1 | 26.02 |
| 2 | 25.91 |
| 3 | 28.13 |
|____|___________|
I want to compare IDs 1,2, and 3 to determine the high value among them. Then once I have compared 1-3, I want to move on to 4 through 6, then 7 through 9, etc until I've done this for all values contained in the table (currently about 400,000 values). I have written code that uses
SELECT max(HIGH) FROM daily_high as dh1 JOIN (SELECT max(HIGH) FROM daily_high WHERE id >= dh1 AND id < (dh1.id + 3))
This works but is horribly slow. I've tried using the SELECT statement where I identify the column values to be pull for display, meaning between the SELECT and FROM parts of the query.
I've tried to use JOIN to join all 3 rows onto the same table for comparison but it too is horribly slow. By slow I mean just under 10 seconds to gather information for 20 rows. This means that the query has analyzed 60 rows (20 groups of 3) in 9.65879893303 seconds (I didn't make this up, I used microtime() to calculate it.
Anyone have any suggestions for faster code than what I've got?
Keep in mind that my actual table is not the same as what I've posted above, but it the concept is the same.
Thanks for any help.
If you ID it continous you can make this
SELECT floor(id/3) as range, max(HIGH) FROM daily_high GROUP BY range;
Why not to use DIV operator for grouping your aggregation:
SELECT (id-1) DIV 3 + 1 AS ID, MAX(high) AS 3MaxHIGH
FROM daily_high
GROUP BY (id-1) DIV 3
This query gives the same result.
ID 3MaxHIGH
1 26.02
2 25.91
3 28.13
I was unable to run your query, and I believe that this one is faster.
UPD: To ensure that you have valid groups for your ranges, use this query:
select id, high, (id-1) div 3 + 1 from daily_high
result:
id high (id-1) div 3 + 1
1 24.65 1
2 24.93 1
3 26.02 1
4 25.33 2
5 25.16 2
6 25.91 2
7 26.05 3
8 28.13 3
9 27.07 3
Fuller answer with an example. The following code will do what I think you want.
SELECT FLOOR((row - 1) / 3), MAX(Sub1.high)
FROM (SELECT #row := #row + 1 as row, daily_high.*
FROM daily_high, (SELECT #row := 0) r) Sub1
GROUP BY FLOOR((row - 1) / 3)
ORDER BY Sub1.ID
The below query worked for me on a test table. perhaps not the best, but the other solutions failed on my test table.
This does require the ID's to be sequential. Also be sure to put an index on High aswell for speed.
SELECT FLOOR(T1.Id/3)+1 AS Id, ROUND(GREATEST(T1.High, T2.High, T3.High),2) AS High FROM `daily_high` T1, `daily_high` T2, `daily_high` T3
WHERE T2.Id=T1.Id+1
AND T3.Id=T2.Id+1
AND MOD(T1.Id, 3)=1
logic: if(id is divisible by 3, id/3-1, id/3)
select if(mod(id,3) = 0,floor(id/3)-1,floor(id/3)) as group_by_col , max(HIGH)
FROM daily_high GROUP BY group_by_col;
I have search for Articles with dates. In MySQL is:
Article:
id | title
1 | first
2 | second
3 | third
4 | fourth
DatesArticle:
id | article_id | from | to
1 | 1 | 10-10-2010 | 11-11-2010
2 | 2 | 11-10-2010 | 12-12-2010
3 | 1 | 13-12-2010 | 12-01-2012
4 | 3 | 11-11-2012 | 12-12-2012
5 | 4 | 02-02-2013 | 02-02-2014
i would like get all Article with dates and sort this by availability.
for example i would like get all Articles and SORT this by dates FROM 12-10-2011 TO 12-01-2012
this should return me:
first (is in range FROM TO - DatesArticle.id = 3)
third (is in range FROM TO - DatesArticle.id = 4)
second (is NOT in range FROM TO)
fourth (is NOT in range FROM TO)
Is this possible with SQL or SQL and PHP? If yes, how?
Use the clause CASE, something like:
SELECT * FROM DatesArticle
ORDER BY CASE
WHEN id=3 AND CURRENT_DATE()<=from and to < CURRENT_DATE()>=to THEN 1
WHEN <condition_2> THEN 2
etc...
ELSE <any other condition>
END
Not saying the above is going to work as it is but it gives you and idea. If you add an example of the query you have tried or how are you building your where clause it would help for better answer.
My idea was to when it's id= 3 assign 1, if id = 4 assign 2, any other value assign 3, after that make the where condition and order by the number that you assign.
Try this:
select *,
case when t1.id=3 then 1 when t1.id=4 then 2 else 3 end as t
from article as t1
join DatesArticle as t2
on t1.id=t2.id
where CURRENT_DATE()<=from
and to < CURRENT_DATE()
order by t
You must first JOIN the tables in order to access article's data.
Then you ORDER on a logical condition (if there's a date within the given range).
SELECT title
FROM Article JOIN DatesArticle
ON (Article.id = DatesArticle.id)
ORDER BY DatesArticle.from > LastDate AND DatesArticle.to < FirstDate DESC;
(I never get ASC and DESC right in logical sorts -- try and see what happens)
My table will hold scores and initials.
But the table wont be ordered.
I can get the total row count easy enough and I know I can get all of them and Order By and then loop through them and get the rank THAT way...
But is there a better way? Could this maybe be done with the SQL statement?
I'm not TOO concerned about performance so if the SQL statement is some crazy thing, then Ill just loop.
Sorry - Table has id as primary key, a string to verify unique app install, a column for initials and a column for score.
When someone clicks GET RANK... I want to be able to tell them that their score is 100 out of 1000 players.
SELECT s1.initials, (
SELECT COUNT(*)
FROM scores AS s2
WHERE s2.score > s1.score
)+1 AS rank
FROM scores AS s1
Do you have a primary key for the table or are you fetching data by the initials or are you just fetching all the data and looping through it to get the single record? How many results are you trying to fetch?
If you only want to fetch one record, as the title suggests, you would query the database using a WHERE conditional:
SELECT score FROM table WHERE user_id = '1';
See this answer: https://stackoverflow.com/a/8684441/125816
In short, you can do something like this:
SELECT id, (#next_rank := IF(#score <> score, 1, 0)) nr,
(#score := score) score, (#r := IF(#next_rank = 1, #r + 1, #r)) rank
FROM rank, (SELECT #r := 0) dummy1
ORDER BY score DESC;
And it will produce a result like this:
+------+----+-------+------+
| id | nr | score | rank |
+------+----+-------+------+
| 2 | 1 | 23 | 1 |
| 4 | 1 | 17 | 2 |
| 1 | 0 | 17 | 2 |
| 5 | 1 | 10 | 3 |
| 3 | 1 | 2 | 4 |
+------+----+-------+------+
Note that users with equal scores have equal ranks. :-)
This is my table structure , Here i need to swap items .Which means, you can see type 3 always comes paired( type 3 items are always paired ).
I just named paired items for understanding first 1 in pair is master and second one is sub. So master of the pair should not come 5,10 and 15 positions
if it comes that place i need to swap the next item into that place(neaxt item will be sub it should not considered as next item)
for example
pid 10 (comes in 10 position) i need to swap it like this
pid type name
.. .. ..
10 2 B2
11 3 E1(master)
12 3 A2(sub)
.. .. ..
Table
pid type pname
1 1 A
2 1 B
3 2 C
4 3 D(mater)
5 3 E(sub)
6 1 A1
7 2 B1
8 1 C1
9 2 D1
10 3 E1(master)
11 3 A2(sub)
12 2 B2
13 1 C2
14 2 D2
15 1 E3
screenshot
perfect placement
Collapsed design
FOR FURTHER HELP
I GIVING YOU TABLE STRUCTURE AND TEST DATA, PLEASE IF YOU HAVE IDEA SHARE WITH ME !
CREATE QUERY
CREATE TABLE IF NOT EXISTS `table_swap1` (
`pid` int(11) NOT NULL AUTO_INCREMENT,
`type` int(11) NOT NULL,
`name` varchar(50) NOT NULL,
PRIMARY KEY (`pid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=17;
INSERT QUERY
INSERT INTO `table_swap1` (`pid`, `type`, `name`) VALUES
(1, 1, 'A'),(2, 1, 'B'),(3, 2, 'D'),(4, 3, 'E(master)'),
(5, 3, 'f(sub)'),(6, 1, 'A1'),(7, 2, 'B1'),(8, 1, 'C1'),
(9, 2, 'D1'),(10, 3, 'E1(master)'),(11, 3, 'A2(sub)'),(12, 2, 'B2'),
(13, 1, 'C2'),(14, 2, 'D2'), (15, 1, 'E2');
My work and Result
SELECT aa.pid, (
CASE aa.pid
WHEN bb.apid
THEN bb.atype
WHEN bb.bpid
THEN bb.btype
WHEN bb.cpid
THEN bb.ctype
ELSE aa.type
END
)
TYPE , (
CASE aa.pid
WHEN bb.apid
THEN bb.aname
WHEN bb.bpid
THEN bb.bname
WHEN bb.cpid
THEN bb.cname
ELSE aa.name
END
)name
FROM (
SELECT a.pid +1 apid, a.TYPE atype, a.NAME aname,
b.pid +1 bpid, b.type btype, b.name bname,
c.pid -2 cpid, c.type ctype, c.name cname
FROM table_swap1 a, table_swap1 b, table_swap1 c
WHERE MOD( a.pid, 5 ) =0
AND a.pid +1 = b.pid
AND a.type =3
AND a.type = b.type
AND a.pid +2 = c.pid
)bb, table_swap1 aa
GROUP BY pid
This Query did what i exactly need
... But In my case pid is Primary key.
so i can't get the results in 1 to 15
order . So can i do this row number...
how can i do
all kind of suggestions are welcome even solution in php
let me is this possible in mysql or any other way to do this ..
So, basically your problem can be formulated as :
The first product of two adjacent products of type 3 cannot be placed
in a position which is a multiple of 5.
What complicates things is that there is no order in your table, and without an order, it is impossible to define a constant "position" for your products. The order of returned rows for a SELECT without ORDER BY is not specified.
Anyway, the simplest way to do this is in the application. Grab your results as an array, scan it, and if you find two products that are not in the right position, simply shuffle them around in the array.
previous solution - bad solution
Edit: I don't think a solution is really possible in this case. Even you it happens possible to reorder items (which will not always be the case) it might happen that items that expire sooner will be after items that expire later.
What you can do:
in all cases you need to use one entry per master+sub pair.
make a table product_list(prodid, subid) where subid can be null for non-product tables
join product table products once to fetch master and second time to fetch sub (tell me if you need the query) and group by product_list.prodid
showing the products - choose one of
make a design that allows you to put 2 items in one square.
put a button "bonus product" with popup or javascript animation showing the other product
on mouseover expand the product to the right or left depending on position.
I think you need to redesign your tables to avoid having to swap rows - remember, order of rows should be immaterial in a relational table, and mySQL is a relational DB.
Here is a possible redesign - though let me know if I don't understand your question correctly:
Table product
-------------
pid
---
name
type
Table productPair
-----------------
pid*
---
part1*
part2*
When a product is a pair in a box, you enter it both as a product, and as a productpair, with the same key. With appropriate constraints, you can model the design so that pairs are only made of one type 1 and one type2 product, etc.
Some example data:
+------------------+
|product |
+------------------+
|PID |type |name |
+----+------+------+
|1 |1 |A |
|2 |1 |B |
|3 |2 |C |
|4 |3 |D |
|5 |3 |E |
+----+------+------+
+------------------+
|productPair |
+------------------+
|PID*|part1*|part2*|
+----+------+------+
|4 |1 |3 |
|5 |2 |3 |
+----+------+------+
In this form, no need for row order, the relations encode the nature of each pair, rather than the position of the data in the table.
Yes, I also suggest you to redesign here, but for quik-n-dirty result just add to your SQL something like this:
select
table_swap1.*,
#row_number := #row_number + 1 as row_number,
0 = mod(#row_number, 5) is_fifth
from
table_swap1,
(SELECT #row_number := 0) tmp
;
My environment is SQL Server. I've written my solution in that syntax...I'll attempt to rewrite in MySQL (below the SQL Server original), but I'm sure you'll understand my point (whether or not I've rewritten correctly). Basically, use the bb subquery to establish an unspecified join (Cartesian product), so that the pid of every master row which falls on a multiple of 5 is available to all other rows, then recalculate the rows that match (and the affected adjoining rows) into a Resequence column. If I understand your data correctly, you might even leave out the type=3 AND phrase.
SELECT (CASE
WHEN aa.pid = bb.pid THEN aa.pid+1
WHEN aa.pid-1=bb.pid THEN aa.pid+1
WHEN aa.pid-2=bb.pid THEN aa.pid-2
ELSE aa.pid END) ResequencePid
, aa.pid, aa.type, aa.name
FROM table_swap1 aa,
(SELECT pid FROM table_swap1 WHERE type=3 AND pid%5=0 AND name LIKE '%(master)') bb
/* optional */ ORDER BY 1
/* MySQL version */
SELECT (CASE
WHEN aa.pid = bb.pid THEN aa.pid+1
WHEN aa.pid-1=bb.pid THEN aa.pid+1
WHEN aa.pid-2=bb.pid THEN aa.pid-2
ELSE aa.pid END) ResequencePid
, aa.pid, aa.type, aa.name
FROM table_swap1 aa,
(SELECT pid FROM table_swap1 WHERE type=3 AND MOD(pid, 5)=0 AND name LIKE '%(master)') bb
/* optional */ ORDER BY 1