I have 3 tables in MySQL ParkIn{ parkin_id,InTime} ,ParkOut{parkout_id,OutTime} and Counter{ Counter_ID, Counter}.
Everytime there is an Insert in the Parkin table I need the Counter column to add 1 to its previous value and whenever there is a Insert in the ParkOut table I need the counter column to subtract 1. The counter table doesnt necessarily have to insert new lines everytime but rather just keep updating the counter value.
You're doing this the hard way.
You can do
SELECT (parkin.rowcount - parkout.rowcount) Counter
FROM (SELECT COUNT(*) rowcount FROM ParkIn) parkin
JOIN (SELECT COUNT(*) rowcount FROM ParkOut) parkout
when you need the value you described. You can trust that databases are made to do this kind of thing.
If you really want the illusion that you have a Counter table, you can create a view.
CREATE OR REPLACE VIEW Counter AS
SELECT (parkin.rowcount - parkout.rowcount) Counter
FROM (SELECT COUNT(*) rowcount FROM ParkIn) parkin
JOIN (SELECT COUNT(*) rowcount FROM ParkOut) parkout
Your Counter table, as you described it in your question, is known as a "materialized view" in the DBMS trade. It's "materialized" because it's an actual table that you actually maintain in real time. I am suggesting using an ordinary view, not a materialized view, to solve your problem.
Related
I am completely new to database coding, and I've tried Googling but cannot seem to figure this out. I imagine there's a simple solution. I have a very large table with MemberIDs and a few other relevant variables that I want to pull records from (table1). I also have a second large table of distinct MemberIDs (table2). I want to pull rows from table 1 where the MemberID exists in table2.
Here’s how I tried to do it, and for some reason I suspect this isn’t working correctly, or there may be a much better way to do this.
proc sql;
create table tablewant as select
MemberID, var1, var2, var3
from table1
where exists (select MemberID from table2)
;
quit;
Is there anything wrong with the way I’m doing this? What's the best way to solve this when working with extremely large tables (over 100 million records)? Would doing some sort of join be better? Also, do I need to change
where exists (select MemberID from table2)
to
where exists (select MemberID from table2 where table1.MemberID = table2.MemberID)
?
You want to implement a "semi-join". You second solution is correct:
select MemberID, var1, var2, var3
from table1
where exists (
select 1 from table2 where table1.MemberID = table2.MemberID
)
Notes:
There's no need to select anything special in the subquery since it's not checking for values, but for row existence instead. For example, 1 will do, as well as *, or even null. I tend to use 1 for clarity.
The query needs to access table2 and this should be optimized specially for such large tables. You should consider adding the index below, if you haven't created it already:
create index ix1 on table2 (MemberID);
The query does not have a filtering criteria. That means that the engine will read 100 million rows and will check each one of them for the matching rows in the secondary table. This will unavoidably take a long time. Are you sure you want to read them all? Maybe you need to add a filtering condition, but I don't know your requirements in this respect.
Hello again internet nerds!
Technically I have solved this problem, but want to know if there is a more optimal route I should take...
I have a large table (~4m rows) that is a collection of data, segmented using int "chip." There are 6 segments of data, so Chip IDs 1 through 6.
Of these 6 segments, I need to assign an order integer, which needs to be iterative, as it represents the exact location of the data on said segment.
My solution is (was) this:
# set iterative
set #i:=0;
# init
update table set `order` = #i:=(#i+1) where chip = 1;
This works. But it is so slow it sometimes triggers a timeout error. I need to run it 6 times and it may be triggered on occasion by our application whenever necessary. Maybe it's just that I need to alot more time to MySQL settings to account for the slow query, or is there an optimal, simpler solution for this?
Thanks for the advice.
Edit:
I've found a solution that works accurately and takes ~50 seconds to complete.
I'm now using an ordered select statement paired in the update, using a generated join table that's iterating within a column.
See:
set #count:= 0;
update
table as target,
(select
(#count := #count+1) as row_num,
t.*
from table as t
where chip = 1
order by t.id asc) as table_with_iterative
set target.`order` = table_with_iterative.row_num
where target.id = table_with_iterative.id;
I think that if possible you should assign the sequence numbers as you are inserting the rows into the table for the very first time. Strictly speaking, an SQL database does not promise that rows will initially appear inside the table in the order of of the INSERT statements.
One possibility that occurs to me is to use an auto-increment field in this table, because this will express the order in which the rows were added. But the field-values for any given chip, although ascending, might not be consecutive and undoubtedly would not start at 1.
If you did need such a field, I think I'd define a separate field (default value: NULL) to hold it. Then, a very simple stored procedure could query the rows for a given chip (ORDER BY auto-increment number) and assign a 1-based consecutive sequential number to that separate field. (A slightly more elaborate query could identify all the lists that don't have those numbers yet, and number all of them at once.)
I've found a solution that works accurately and takes ~50 seconds to complete.
This handles the out-of-order updates I was experiencing.
I'm now using an ordered select statement paired in the update statement, using a generated join table that's iterating within a column row_num.
See:
set #count:= 0;
update
table as target,
(select
(#count := #count+1) as row_num,
t.*
from table as t
where chip = 1
order by t.id asc) as table_with_iterative
set target.`order` = table_with_iterative.row_num
where target.id = table_with_iterative.id;
im having trouble with update if selected table is true...
IF this select statement is true
(SELECT COUNT(*) FROM Add_bonus WHERE value > 0) > 0)
THEN do
UPDATE bonus SET amount = amount + 10 WHERE 1
The if statement is only allowed in stored procedures, functions, and triggers in MySQL. You can do this with a single update:
UPDATE bonus
SET amount = amount + 10
WHERE exists (select 1 from Add_Bonus where value > 0);
Note that this query will update all rows in the bonus table when any appropriate row exists in the add_bonus table. That appears to be the intention of your original query. More commonly, there might be an employee id linking the two tables.
Also note the use of exists. This is typically more efficient than using count(*), because it can stop processing on the first row that matches.
I know there is a few posts out there already but some are conflicting.
I have taken on a project in which I have inherited a table with a few 1000 entries.
The problem is, there is no auto increment ID field on the table and I have been asked to extract the last 300 rows that were entered into it.
It it possible to extract the last 300 entries from a table? Is there a "systems row id"?
The strict answer is "no" unless you have a date or something else that indicates order. Tables are inherently unordered.
In practice, you generally fetch the data back in the order you put it in. The more true the statement, "I loaded the data once, with no subsequent inserts, into a system with only one processor and one disk", the more likely that the data is actually in order.
Having a system row id would not help you, because you might have deletes and subsequent inserts. A later record would be put in an earlier page, in this case.
You have a small table. Do a select *, copy the data into a spreadsheet and do the work from there.
Alternatively, you can select the table with an increasing row number, insert into another table, and then do the select from there. Something like this pseudocode:
insert into NewTable (seqnum, cols)
select :rownum=:rownum+1, cols
from YourTable
There is a chance you'll get what you want.
One last point. If you did inserts and have the log files since the inserts, you might be able to get the information from there. With a little work.
Try this:
SELECT col1, col2, ...
FROM (SELECT col1,col2, ..., (#auto:=#auto+1) indx
FROM tablename, (SELECT #auto:=1) AS a
) AS b
ORDER BY indx DESC
LIMIT 30
If you at least have a record insertion time in the table, then you can use this.. Otherwise no.
SELECT * FROM
yourtable
ORDER BY inserted_time desc limit 300;
I have a table that has a "view_count" column.
I would like to increment said column every time I select the row (even with a generic SELECT, such as SELECT * FROM table WHERE 1.. This would increment by 1 the view_count of all rows).
Is there a way to do it automatically "server-side" (where mysql is the server and my application is the client), ie without an UPDATE every time?
EDIT: Since a couple of people asked me why I wanted to do this, and a few misunderstood my request, imagine that's a forum software, and the table is the thread table. Each thread has a view count, and you want to update it every time you display the thread in the main page (I know usually the threads' view count are only updated when you actually view it, but it's the best example i could come up with, my particular case is kinda long and complicated T_T)
Can't be done. You basically want a trigger on SELECT, and triggers are only supported for INSERT, UPDATE, and DELETE. The closest you could get would be to run all your interactions with the table through a stored procedure, and it sounds like you want this behavior to be enforced under all conditions.
Why not just issue one SQL statement like so:
UPDATE tableName SET viewCount = viewCount + 1; SELECT tableName.col1, tableName.col2;
That could even be dynamically created with ease in any language.
You can do it in the session scope:
SET #view_count := 0;
SELECT v.*,
#view_count := #view_count + 1 AS view_count
FROM view
You will have an incrementing value for each row viewed:
SELECT q.*,
#view_count := #view_count + 1 AS view_count
FROM (
SELECT 1 AS col
UNION ALL
SELECT 2 AS col
) q;
-- --
1 1
2 2
SELECT q.*,
#view_count := #view_count + 1 AS view_count
FROM (
SELECT 1 AS col
UNION ALL
SELECT 2 AS col
) q;
-- --
1 3
2 4
This, however, will only count the views within your session, not the total views.
I don't believe there's a simple way to do this server-side; triggers only execute on UPDATEs, INSERTs, and DELETEs.
Stored procedures, but it's a pain. I would implement security elsewhere.