Django: Saving to database connundrun - mysql

I have the following within my view which works perfectly:
if request.method == 'POST':
form = SelectTwoTeams(request.POST,user=request.user)
if form.is_valid():
teamSelection1 = UserSelection(campaignno = 102501350,
teamselection1or2 = 1,
teamselectionid_id = request.POST['team1'],
user_id=currentUserID,
fixturematchday=fixturematchday,
soccerseason_id=soccerseason)
teamSelection1.save()
teamSelection2 = UserSelection(campaignno = 102501350,
teamselection1or2 = 2,
teamselectionid_id = request.POST['team2'],
user_id=currentUserID,
fixturematchday=fixturematchday,
soccerseason_id=soccerseason)
teamSelection2.save()
When the code is run it ends up nicely in my database as it should:
mysql> select * from straightred_userselection;
+-----------------+------------+-------------------+-----------------+---------+-----------------+----------------+
| userselectionid | campaignno | teamselection1or2 | teamselectionid | user_id | fixturematchday | soccerseasonid |
+-----------------+------------+-------------------+-----------------+---------+-----------------+----------------+
| 9 | 102501350 | 1 | 6 | 349 | 2 | 1025 |
| 10 | 102501350 | 2 | 7 | 349 | 2 | 1025 |
+-----------------+------------+-------------------+-----------------+---------+-----------------+----------------+
2 rows in set (0.00 sec)
However, lets just say I now want to choose two different teams (currently 6 & 7 under the teamselectionid column). I can select them in my form and update but when the code is run it just adds another two rows to the table rather than update the current ones. The check to see if it needs to be a new set of team selections of an update is simply if:
fixturematchday and soccerseasonid are then same then update
fixturematchday and soccerseasonid are NOT the same then create new entries
As mention above it currently just adds two new rows as per below:
mysql> select * from straightred_userselection;
+-----------------+------------+-------------------+-----------------+---------+-----------------+----------------+
| userselectionid | campaignno | teamselection1or2 | teamselectionid | user_id | fixturematchday | soccerseasonid |
+-----------------+------------+-------------------+-----------------+---------+-----------------+----------------+
| 9 | 102501350 | 1 | 6 | 349 | 2 | 1025 |
| 10 | 102501350 | 2 | 7 | 349 | 2 | 1025 |
| 11 | 102501350 | 1 | 9 | 349 | 2 | 1025 |
| 12 | 102501350 | 2 | 14 | 349 | 2 | 1025 |
+-----------------+------------+-------------------+-----------------+---------+-----------------+----------------+
4 rows in set (0.00 sec)
Any help would be greatly appreciated, many thanks, Alan.

That's because you are creating new objects each time. Use update_or_create instead: https://docs.djangoproject.com/en/dev/ref/models/querysets/#update-or-create

Let me explain more about update_or_create, in your case, what you want is if the teamselectionid existed, just update that object.So should be
obj, created = UserSelection.objects.update_or_create(
teamselectionid=teamselectionid, defaults=what_field_you_want_to_update)
EDIT
The parameters before defaults just let you find the unique row in your database, For instance, a user may choose many teams, so only use user_id can't find the unique row, but you can combine user_id and team_id, like:
obj, created = UserSelection.objects.update_or_create(
user_id=user_id, team_id=team_id, defaults=what_field_you_want_to_update)

Related

MYSQL - How to merge 2 rows of related data into 1

I am new to mysql and tried to research it but couldn't find any solution. I have a table like this:
| SW_Pair1 | SW_Pair2 | Pair1_VLAN1| Pair1_VLAN2| Pair2_VLAN1| Pair2_VLAN2| Inter | Mgmt| OSPF| Env | Domain|
|-----------------|-----------------|------------|------------|------------|------------|-------|-----|-----|-----|-------|
| Switch1.abc.com | Switch2.abc.com | VLAN-111 | VLAN-333 | Unknown | Unknown | 47 | 24 | 0.1 | Dev | abc |
| Switch2.abc.com | Switch1.abc.com | VLAN-222 | VLAN-444 | Unknown | Unknown | 47 | 24 | 0.1 | Dev | abc |
| Switch3.abc.com | Switch4.abc.com | VLAN-121 | VLAN-123 | Unknown | Unknown | 47 | 24 | 0.1 | Dev | abc |
| Switch4.abc.com | Switch3.abc.com | VLAN-515 | VLAN-717 | Unknown | Unknown | 47 | 24 | 0.1 | Dev | abc |
| Switch5.abc.com | Switch6.abc.com | VLAN-919 | VLAN-101 | Unknown | Unknown | 47 | 24 | 0.1 | Dev | abc |
| Switch6.abc.com | Switch5.abc.com | VLAN-105 | VLAN-108 | Unknown | Unknown | 47 | 24 | 0.1 | Dev | abc |
| Switch7.abc.com | Switch8.abc.com | VLAN-110 | VLAN-115 | Unknown | Unknown | 47 | 24 | 0.1 | Dev | abc |
| Switch8.abc.com | Switch7.abc.com | VLAN-199 | VLAN-200 | Unknown | Unknown | 47 | 24 | 0.1 | Dev | abc |
Lets take first 2 rows as an example.
SW_Pair1 in row 1 == SW_Pair2 in row 2
SW_Pair1 in row 2 == SW_Pair2 in row 1
I put them in next row but they can be anywhere in database. Now I would like to merge these 2 so that data in Pair1_VLAN1 and Pair1_VLAN2 in row 2 goes in Pair2_VLAN1 and Pair2_VLAN2 of row 1 and then row 2 disappears. So, here is how the table should look after merge:
| SW_Pair1 | SW_Pair2 | Pair1_VLAN1| Pair1_VLAN2| Pair2_VLAN1| Pair2_VLAN2| Inter | Mgmt| OSPF| Env | Domain|
|-----------------|-----------------|------------|------------|------------|------------|-------|-----|-----|-----|-------|
| Switch1.abc.com | Switch2.abc.com | VLAN-111 | VLAN-333 | VLAN-222 | VLAN-444 | 47 | 24 | 0.1 | Dev | abc |
| Switch3.abc.com | Switch4.abc.com | VLAN-121 | VLAN-123 | VLAN-515 | VLAN-717 | 47 | 24 | 0.1 | Dev | abc |
and so on ..
I am using python 2.7 to push data to sql.
Edit:
I tried below query to add additional checks on DELETE but it failed:
UPDATE yourTable AS a
DELETE FROM yourTable AS b ON a.SW_Pair1 = b.SW_Pair2 AND a.SW_Pair2 = b.SW_Pair1
WHERE Pair2_VLAN1 IS Unknown;
Or better, can it SET the values of Pair1_VLAN1 and Pair1_VLAN2 rows of second switch after it moves it data to switch 1? Maybe over-write the vlan to something like "MERGED". I can then safely remove anything that has "MERGED" in Pair1_VLAN1 and Pair1_VLAN2. I know it will only say that when it's data was successfully got moved to another row.
EDIT2:
nvm .. figured it out. See below:
UPDATE yourTable AS a
JOIN yourTable AS b ON a.SW_Pair1 = b.SW_Pair2 AND a.SW_Pair2 = b.SW_Pair1
SET a.Pair2_VLAN1 = b.Pair1_VLAN1,
a.Pair2_VLAN2 = b.Pair1_VLAN2,
b.Pair1_VLAN1 = "MERGED",
b.Pair1_VLAN2 = "MERGED
WHERE a.SW_Pair1 < a.SW_Pair2;
First update the first row in each pair with the data from the matching row:
UPDATE yourTable AS a
JOIN yourTable AS b ON a.SW_Pair1 = b.SW_Pair2 AND a.SW_Pair2 = b.SW_Pair1
SET a.Pair2_VLAN1 = b.Pair1_VLAN1,
a.Pair2_VLAN2 = b.Pair1_VLAN2
WHERE a.SW_Pair1 < a.SW_Pair2;
The WHERE clause ensures that only one row in each pair (the one with the lower name in SW_Pair1) is updated.
Then delete the rows that weren't updated. They will still have NULL in the columns that were updated by the first query.
DELETE FROM yourTable
WHERE Pair2_VLAN1 IS NULL;
This assumes that there are matching rows for everything. If you need something safer, you'll need to do a join that checks that there's a matching row with the opposite names.
DELETE a FROM yourTable AS a
JOIN yourTable AS b ON a.SW_Pair1 = b.SW_Pair2 AND a.SW_Pair2 = b.SW_Pair1
WHERE a.Pair2_VLAN1 IS NULL
I'm sure there is a cleaner way to do this, but this is a hacky way I came up with.
select
a.sw_pair1,
a.sw_pair2,
a.pair1_vlan1,
a.pair2_vlan1 as pair1_vlan2,
b.pair1_vlan1 as pair2_vlan1,
b.pair2_vlan1 as pair2_vlan2
from TABLENAME a
join TABLENAME b on a.sw_pair1 = b.sw_pair2
where cast(substring_index(substring_index(a.sw_pair1, '.abc.com', 1), 'Switch', -1) as unsigned) % 2 > 0
I'm using the modulo (% 2) to make sure we get the odd numbers in the first column only, therefore having the even numbers in column 2. I'd be curious to see if someone else can come up with a cleaner solution for that than I did. If so, that would help me with some of the things I do from time to time.
This method worked for me and seems simpler than the current answers. This gave your desired output from the sample data.
SELECT
a.SW_Pair1,
a.SW_Pair2,
a.Pair1_VLAN1,
a.Pair1_VLAN2,
b.Pair1_VLAN1 as Pair2_VLAN1,
b.Pair1_VLAN2 as Pair2_VLAN2
FROM test as a , test as b
WHERE a.SW_Pair1 = b.SW_Pair2 AND a.SW_Pair2>b.SW_Pair2;
If you want to store the merged data into table, then #Barmar's solution will work perfectly.
But if you just want to display the data then following query will get the job done:
select least(t1.SW_Pair1,t1.SW_Pair2),greatest(t1.SW_Pair1,t1.SW_Pair2),
t2.Pair1_VLAN1,t2.Pair1_VLAN2,
t1.Pair1_VLAN1 as Pair2_VLAN1,t1.Pair1_VLAN2 as Pair2_VLAN2
from tablet1 as t1
inner join tablet2 as t2
on t2.SW_Pair1 = t1.SW_Pair2 and t2.SW_Pair2=t1.SW_Pair1
group by least(t1.SW_Pair11,t1.SW_Pair2),greatest(t1.SW_Pair1,t1.SW_Pair2);
Hope it helps!

mysql table having a->b and b->a values, select only a->b set of values

I have one table having 5 columns
linkid, orinodeno, orinodeno, ternodeno, terifindex
linkid is autoincremented. orinodeno, oriifindex is one combination value and ternodeno, terifindex other combination (orinodeno,oriifindex is originating value and ternodeno,terifindex terminating value i.e, in between there is a link eg just like map two pts n in between connecting link) so my table contains a->b values (i.e a is combination of orinodeno, oriifindex and b is combination of ternodeno,terifindex) and b->a values. so I have to select only a->b set of values not b->a. Also sending my table image. My Table
There is no a map definition in sql databases, forget it. Check any database normalization tutorial. Then you shouldn't have any problems with select statements.
Please be clear about what you are asking. If you can not explain in words, please give example input and your expected output.
From link of table image you have provided and description, It looks like you expect following:
Data in current table:
------------------------------------------------------------------
|linkid | orinodenumber | oriifindex | ternodenumber | terifindex|
------------------------------------------------------------------
|305 | 261 | 2 | 309 | 2 |
|306 | 309 | 2 | 261 | 2 |
|307 | 257 | 10 | 310 | 10 |
|308 | 310 | 10 | 257 | 10 |
|309 | 257 | 11 | 310 | 11 |
------------------------------------------------------------------
Expected Output:
------------------------------------------------------------------
|linkid | orinodenumber | oriifindex | ternodenumber | terifindex|
------------------------------------------------------------------
|305 | 261 | 2 | 309 | 2 |
|307 | 257 | 10 | 310 | 10 |
------------------------------------------------------------------
If that is your case, following query might help you (Assuming table name as link_table):
SELECT *
FROM link_table o
WHERE EXISTS (SELECT linkid
FROM link_table i
WHERE o.orinodenumber = i.ternodenumber
AND o.oriifindex = i.terifindex
AND o.linkid < i.linkid);

Issue with UNION in MySQL

I have two tables.
rp_format
+-----+--+--------------+
| fid | | recordformat |
+-----+--+--------------+
| 1 | | CD |
| 2 | | Vinyl |
| 3 | | DVD |
+-----+--+--------------+
rp_records
+----+--+--------+
| id | | format |
+----+--+--------+
| 1 | | 1 |
| 2 | | 2 |
| 3 | | 3 |
+----+--+--------+
What I would like to achieve is to display everything from "rp_format". But I would also like make a check to see if there is a "fid"-value found in "format".
Example that should be displayed on page like this:
fid recordformat
1 CD Remove this format
2 Vinyl Remove this format
3 DVD Remove this format
But let's say an "fid" value is found in "format" then I would like it to be displayed like this on page:
fid recordformat
1 CD Remove this format
2 Vinyl Can't remove this format
3 DVD Remove this format
"Remove this format / Can't remove this format" is text that will be displayed by checking if "fid" = "format" using PHP.
Here is my SQL query so far:
global $wpdb;
$rpdb = $wpdb->prefix . 'rp_format';
$rpdb2 = $wpdb->prefix . 'rp_records';
$sql = "
SELECT *
FROM $rpdb
LEFT OUTER JOIN $rpdb2 ON $rpdb.fid = $rpdb2.format
UNION
SELECT *
FROM $rpdb
RIGHT OUTER JOIN $rpdb2 ON $rpdb.fid = $rpdb2.format
WHERE $rpdb.fid IS NOT NULL
";
The issue I have with this query is that when "fid" is found in "format" (let's say it's found 10 times) every of these 10 values will be outputed also.
How can this be fixed?
Kind regards
Johan
If I understand correctly you want to display some message depending on if the data exists on rp_records or not and avoid multiple display.
Consider the following
mysql> select * from rp_format;
+------+--------------+
| fid | recordformat |
+------+--------------+
| 1 | CD |
| 2 | Vinyl |
| 3 | DVD |
| 4 | Test |
+------+--------------+
4 rows in set (0.00 sec)
mysql> select * from rp_records;
+------+--------+
| id | format |
+------+--------+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 2 |
| 5 | 1 |
+------+--------+
So the query is
select
f.*,
case
when r.format is not null then 'Can\'t remove' else 'Remove this' end
as message
from rp_format f
left join rp_records r on r.format = f.fid
group by f.fid ;
+------+--------------+--------------+
| fid | recordformat | message |
+------+--------------+--------------+
| 1 | CD | Can't remove |
| 2 | Vinyl | Can't remove |
| 3 | DVD | Can't remove |
| 4 | Test | Remove this |
+------+--------------+--------------+
Not sure that i correctly understand your logic with found and not found format, if i wrong - add to if condition r.format IS NOT NULL instead r.format IS NULL. And i think you no need to use union, you should use join:
SELECT
r.fid,
f.recordformat,
IF(r.format IS NULL, "Can't remove this format", "Remove this format")
FROM rp_format f
LEFT JOIN rp_records r ON f.fid = r.format
GROUP BY f.fid
;
I'm sure that something like this will help you!

SQL join several tables based on latest entry in transaction table per join record

I have a transaction table with timestamps
a transaction has one event and one user.
All transactions have an event,
All events have at least one trasaction,
Each transaction has a user that must exist,
A User will not necessarily have a transaction.
The output will be a sort of the evt list
Output line count should equal db.evt record count.
The first column of each table is the Autoinc unique index.
In transaction, these are fks to the other tables.
The problem is that I need the transaction with the latest timestamp for the evt in the transaction table.
I am still relatively new to SQL (Using MySQL) and while I muddle through joins. I have no idea how to get the latest record by evID by timestamp.
I have looked at other questions on the topic but not found one that addresses mine. (Granted there are 14K on Joins alone, so I may have missed one)
Sample Table Data below:
Table structure is hopefully obvious by I will edit it in if requested.
Edit:
I've changed the names of tables and columns for clarity (and to avoid matching keywords)
I tried Stuart's answer below and got an error:
Answer:
SELECT
eventTable.EvtName AS EvtD,
transTable.TranAct AS LastTrans,
userTable.UserName AS UsrNm
FROM
transTables,
INNER JOIN (
SELECT evtID, MAX(TransID) TransID FROM transTable GROUP BY evtID
) last ON last.evtID = transTable.evtID AND last.TransID = transTable.TransID
INNER JOIN eventTable ON eventTable.evtID = transTable.evtID
INNER JOIN userTable ON userTable.usId = transTable.usId
Response:
#1064 - You have an error in your SQL syntax;
check the manual that corresponds to your MySQL server version for the right syntax to use near
'INNER JOIN (
SELECT evtID, MAX(TransID) TransID FROM transTable GROUP BY evt'
at line 7
Tables:
db.transTable
| TransID | EvtID | TranAct | timestamp | UserID
----------------------------------------------------------------
| 1 | 1 | add | 2014-05-08 08:10:00.000 | 3
| 2 | 2 | add | 2014-05-08 09:10:00.000 | 2
| 3 | 3 | add | 2014-05-08 10:10:00.000 | 3
| 4 | 2 | validate | 2014-05-08 11:10:00.000 | 5
| 5 | 3 | validate | 2014-05-08 12:10:00.000 | 3
| 6 | 2 | reverse | 2014-05-08 13:10:00.000 | 1
| 7 | 1 | edit | 2014-05-08 14:10:00.000 | 4
| 8 | 4 | add | 2014-05-08 15:10:00.000 | 3
| 9 | 5 | add | 2014-05-08 16:10:00.000 | 2
db.eventTable
| EvtID | EvtName
-----------------
| 1 | Evt1
| 2 | Evt2
| 3 | Evt3
| 4 | Evt4
| 5 | Evt5
db.userTable
| UserID | UserName
--------------------
| 1 | Usr1
| 2 | Usr2
| 3 | Usr3
| 4 | Usr4
| 5 | Usr5
Desired output:
eventTable.EvtName AS EvtD
transTable.TranAct AS LastTrans
userTable.UserName AS UsrNm
| EvtD | LastTrans | UsrNm
--------------------------
| Evt1 | edit | Usr4
| Evt2 | reverse | Usr1
| Evt3 | validate | Usr3
| Evt4 | add | Usr3
| Evt5 | add | Usr2
Much thanks for any assistance.
Something like this shuold work where a derived table is used to eliminate all transactions except the latest per evId.
SELECT
eventTable.EvtName AS EvtD,
transTable.TranAct AS LastTrans,
userTable.UserName AS UsrNm,
FROM
transTable
INNER JOIN (
SELECT evId, MAX(UID) uid FROM transTable GROUP BY evId
) last ON last.evId = transTable.evId AND last.uid = transTable.uid
INNER JOIN eventTable ON eventTable.evId = transTable.evId
INNER JOIN userTable ON userTable.usId = transTable.usId

Getting count of insert/update rows from ON DUPLICATE KEY UPDATE

I have a statement that tries to insert a record and if it already exists, it simply updates the record.
INSERT INTO temptable (col1,col2,col3)
VALUES (1,2,3)
ON DUPLICATE KEY UPDATE col1=VALUES(col1), col2=VALUES(col2), col3=VALUES(col3);
The full statement has multiple inserts and I'm looking to count number of INSERTs against the UPDATEs. Can I do this with MySQL variables, I've yet to find a way to do this after searching.
From Mysql Docs
In the case of "INSERT ... ON DUPLICATE KEY UPDATE" queries, the return value will be 1 if an insert was performed, or 2 for an update of an existing row.
Use mysql_affected_rows() after your query, if INSERT was performed it will give you 1 and if UPDATE was performed it will give you 2.
I've accomplished what you're describing using a while loop so that each iteration creates a MySQL statement that affects one row. Within the loop, I run the mysql_affected_rows() and then increment a counter depending upon whether the value returned was a 0 or a 1. At the end of the loop, I echo both variables for viewing.
The complete wording from MySQL Docs regarding the mysql_affected_rows function is (notice there are 3 possible values returned - 0, 1, or 2):
For INSERT ... ON DUPLICATE KEY UPDATE statements, the affected-rows
value per row is 1 if the row is inserted as a new row, 2 if an
existing row is updated, and 0 if an existing row is set to its
current values. If you specify the CLIENT_FOUND_ROWS flag, the
affected-rows value is 1 (not 0) if an existing row is set to its
current values.
(Sidenote - I set $countUpdate and $countInsert and $countUpdateNoChange to 0 prior to the while loop):
Here's the code that I developed that works great for me:
while (conditions...) {
$sql = "INSERT INTO test_table (control_number, name) VALUES ('123', 'Bob')
ON DUPLICATE KEY UPDATE name = 'Bob'";
mysql_query($sql) OR die('Error: '. mysql_error());
$recordModType = mysql_affected_rows();
if ($recordModType == 0) {
$countUpdateNoChange++;
}elseif($recordModType == 1){
$countInsert++;
}elseif($recordModType == 2){
$countUpdate++;
};
};
echo $countInsert." rows inserted<br>";
echo $countUpdateNoChange." rows updated but no data affected<br>";
echo $countUpdate." rows updated with new data<br><br>";
Hopefully, I haven't made any typos as I've recreated it to share while removing my confidential data.
Hope this helps someone. Good luck coding!
I know this is a bit old, but I was doing a bulk insert in PHP and needed to know exactly how many rows were inserted and updated (separately).
So I used this:
$dataCount = count($arrData); // number of rows in the statement
$affected = mysql_affected_rows(); // mysqli_*, PDO's rowCount() or anything
$updated = $affected - $dataCount;
$inserted = 2 * $dataCount - $affected;
Simple trace table:
-------------------------------
| data | affected | ins | upd |
-------------------------------
| 1 | 1 | 1 | 0 |
-------------------------------
| 2 | 2 | 2 | 0 |
| 2 | 3 | 1 | 1 |
| 2 | 4 | 0 | 2 |
-------------------------------
| 3 | 3 | 3 | 0 |
| 3 | 4 | 2 | 1 |
| 3 | 5 | 1 | 2 |
| 3 | 6 | 0 | 3 |
-------------------------------
| 4 | 4 | 4 | 0 |
| 4 | 5 | 3 | 1 |
| 4 | 6 | 2 | 2 |
| 4 | 7 | 1 | 3 |
| 4 | 8 | 0 | 4 |
-------------------------------
| 5 | 5 | 5 | 0 |
| 5 | 6 | 4 | 1 |
| 5 | 7 | 3 | 2 |
| 5 | 8 | 2 | 3 |
| 5 | 9 | 1 | 4 |
| 5 | 10 | 0 | 5 |
-------------------------------
if you want to get the number of records that have been inserted and updated separetly, you are to issue each statement separetly.