SQL server change table query not working as expected - sql-server-2008

It looks like that a CHANGETABLE query for a particular table is not working correctly.
I created a record in the bill_details table and then updated it.
The record was created with sys_change_version=5000.
The records was updated with sys_change_version=5001.
But the following CHANGETABLE queries are confusing me and apparently returning incorrect sys_change_creation_version:
select * from CHANGETABLE(CHANGES bill_details, 5000) ct order by bill_id;
sys_change_version sys_change_creation_version sys_change_operation sys_change_columns sys_change_context bill_id
------------------- ----------------------------- ---------------------- ------------------- -------------------- --------
5001 5001 U NULL NULL 2018
select * from CHANGETABLE(CHANGES bill_details, 4999) ct order by bill_id;
sys_change_version sys_change_creation_version sys_change_operation sys_change_columns sys_change_context bill_id
------------------- ----------------------------- ---------------------- ------------------- -------------------- --------
5001 5001 I NULL NULL 2018
I would expect that if the sys_change_operation is 'U' then the sys_change_creation_version would be NULL.
And if a record is updated and if I am searching using a smaller 'from version' number then the sys_change_version and sys_change_creation_versions would be different.
Any ideas what I am missing? Or why these CHANGETABLE queries are behaving in an unexpected manner?
Regards.

Related

update rate for unique productId by each userID

I'm going to implement a method on my own SQL. I have two tables in MySQL. Suppose that each row is updated in the FirstTable and the values of the rate and countView are variable, I'm trying to update them with the same command:
UPDATE FirstTable SET `countView`= `countView`+1,
`rate`=('$MyRate' + (`countView`-1)*`rate`)/`countView`
WHERE `productId`='$productId'
FirstTable:
productId | countView | rate | other column |
------------+-----------+------+-------------------+---
21 | 12 | 4 | anything |
------------+-----------+------+-------------------+---
22 | 18 | 3 | anything |
------------+-----------+------+-------------------+---
But in this way, a user can vote every time he wants to. So I tried to create a table with two columns productId and userID. Like below:
SecondTable:
productId | userID |
------------+---------------|
21 | 100001 |
------------+---------------|
22 | 100002 |
------------+---------------|
21 | 100001 |
------------+---------------|
21 | 100003 |
------------+---------------|
Now, as in the example given in the SecondTable, a user has given to a productId two vote. So I don't want both of these votes to be recorded.
Problems with this method:
The value of the counter is added to each vote.
I can not properly link the SecondTable and FirstTable to manage the update of the FirstTable.
Of course, this question may not be completely new, but I searched a lot to get the right answer. One of the questions from this site came through this method. Using this method, you can manage the update of a table. This method is as follows:
UPDATE `FirstTable` SET `countView`= `countView`+1,
`rate`=('$MyRate' + (`countView`-1)*`rate`)/`countView`
WHERE `productId`='$productId' IN ( SELECT DISTINCT productId, userID
FROM SecondTable)
But the next problem is that even when I use this command, I encounter the following error:
1241 - Operand should contain 1 column(s)
So thank you so much if you can guide me. And I'm sure my question is not duplicate... thank you again.
This fixes your specific syntax problem:
UPDATE FirstTable
SET countView = countView + 1,
rate = ($MyRate + (countView - 1) * rate) / countView
WHERE productId = $productId AND
productId IN (SELECT t2.productId FROM SecondTable t2);
But if two different users vote on the same product, FirstTable will be updated only once. It is unclear if that is intentional behavior or not.
Note that SELECT DISTINCT is not needed in the subquery.
The error is being generated because you can't return 2 fields in an "in" statement. You'll want to use group by:
Try:
IN ( SELECT DISTINCT productId FROM rating group by product, UserID)
Here's documentation to look over for mysql group by if you want: https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html

How is mysql interpreting/grouping a duplicate WHERE statement with an OR statement in the middle?

I am using an Laravel's ORM. It is generating a huge query and inside that query I noticed that there is a WHERE statement that repeats itself, and the order of that WHERE statement seems to be very important. I believe it has something to do with how MySQL is grouping the WHERE statements but I don't understand how MySQL works well enough. I'm trying to understand why this works the way it does. How is mysql interpreting/grouping this?
Table
items
-----------------------------------------------
id | status
-----------------------------------------------
19 | 1
20 | 0
21 | 1
Results needed:
-----------------------------------------------
id | status
-----------------------------------------------
19 | 1
21 | 1
The query is much longer than this. But here is the code the ORM is generating that produces the above results needed:
SELECT * FROM campaigns WHERE status = 1 OR id IN ('20') AND status = 1 ORDER BY id DESC;
If I remove the last status = 1 the query does not return the needed results. Is MySQL grouping the WHERE statements like this:
SELECT * FROM campaigns WHERE status = 1 OR (id IN ('20') AND status = 1);
The query the ORM produces is a few pages long, so when reading this it is pretty confusing without the parentheses. It seems like MySQL is grouping it like this. I guess I don't understand well enough how MySQL works. Any recommendations on books to better understand MySQL?
Building up the Query/Trying to understand what MySQL is doing
1)
SELECT * FROM items WHERE status = 1
Results
-----------------------------------------------
id | status
-----------------------------------------------
19 | 1
21 | 1
2)
SELECT * FROM items WHERE status = 1 OR id IN ('20')
Results
-----------------------------------------------
id | status
-----------------------------------------------
19 | 1
20 | 0
21 | 1
3)
SELECT * FROM items WHERE status = 1 OR id IN ('20') AND status = 1
Results
-----------------------------------------------
id | status
-----------------------------------------------
19 | 1
21 | 1
AND has precedence over OR, see also SQL Logic Operator Precedence: And and Or.
For your example, this means
SELECT * FROM campaigns WHERE status = 1 OR id IN ('20') AND status = 1 ;
is automatically interpreted as
SELECT * FROM campaigns WHERE status = 1 OR (id IN ('20') AND status = 1);
even if you don't put the parenthesis.
It is a good idea to always write the parenthesis, even if you know they are not needed, to make the intention clear to other readers of your code (and to the compiler/interpreter, if needed).

Select all the (varied number of ) rows with the latest date

A MySQL database with data as follows:
project_id | updated | next_steps
1 | 2014-08-01 03:19:20 | new_com
2 | 2014-08-12 03:20:34 | NULL
3 | 2014-08-12 07:01:12 | NULL
4 | 2014-08-05 09:25:45 | comment
I want to select all the rows with the latest date in the column of 'update'. The difference in hours/minutes should be ignored. I expected to get the row 2 and row 3 from this example as follows:
2 | 2014-08-12 03:20:34 | NULL
3 | 2014-08-12 07:01:12 | NULL
Of course, for the real table, the number of rows meet my criteria is changed daily and the numbers could be 100, 200, 324, etc. (it is not a fixed number). I have tried the following code and always get errors.
SELECT * FROM `table` WHERE updated LIKE %DATE(MAX(updated))%;
or
SELECT * FROM `table` WHERE updated LIKE %CAST(DATE(MAX(updated)) AS CHAR)%;
Error message is
"#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 '%CAST(DATE(MAX(updated)) AS CHAR)% LIMIT 0, 30' at line 1"
SELECT MAX(DATE(updated)) FROM table(this returns the 2014-08-12) use this as sub query. This gives back the max date. For example: SELECT * FROM table WHERE DATE(updated) = (SELECT MAX(DATE(updated)) FROM table) The sub query gives back the max date you want, after that you can query the right rows. This returns all the lines that were updated at the max date.
You need to use a WHERE query and use DATE(x) to calculate the maximum date without time and then select all values with that date without time.
Try this:
SELECT * FROM `table` WHERE DATE(`updated`) = (SELECT MAX(DATE(`updated`)) FROM `table`)
And if you still want them ordered
SELECT * FROM `table`
WHERE DATE(`updated`) = (SELECT MAX(DATE(`updated`)) FROM `table`) ORDER BY `updated` DESC
Happy Coding!
if you want two data,
SELECT * FROM `table` order by 'updated' desc limt 2

MySQL counting number of max groups

I asked a similar question earlier today, but I've run into another issue that I need assistance with.
I have a logging system that scans a server and catalogs every user that's online at that given moment. Here is how my table looks like:
-----------------
| ab_logs |
-----------------
| id |
| scan_id |
| found_user |
-----------------
id is an autoincrementing primary key. Has no real value other than that.
scan_id is an integer that is incremented after each successful scan of all users. It so I can separate results from different scans.
found_user. Stores which user was found online during the scan.
The above will generate a table that could look like this:
id | scan_id | found_user
----------------------------
1 | 1 | Nick
2 | 2 | Nick
3 | 2 | John
4 | 3 | John
So on the first scan the system found only Nick online. On the 2nd it found both Nick and John. On the 3rd only John was still online.
My problem is that I want to get the total amount of unique users connected to the server at the time of each scan. In other words, I want the aggregate number of users that have connected at each scan. Think counter.
From the example above, the result I want from the sql is:
1
2
2
EDIT:
This is what I have tried so far, but it's wrong:
SELECT COUNT(DISTINCT(found_user)) FROM ab_logs WHERE DATE(timestamp) = CURDATE() GROUP BY scan_id
What I tried returns this:
1
2
1
The code below should give you the results you are looking for
select s.scan_id, count(*) from
(select distinct
t.scan_id
,t1.found_user
from
tblScans t
inner join tblScans t1 on t.scan_id >= t1.scan_id) s
group by
s.scan_id;
Here is sqlFiddle
It assumes the names are unique and includes current and every previous scans in the count
Try with group by clause:
SELECT scan_id, count(*)
FROM mytable
GROUP BY scan_id

How to select all users for which given parameters are always true

I have a table containing users and locations where they were seen:
user_id | latitude | longitude | date_seen
-------------------------------------------
1035 | NULL | NULL | April 25 2010
1035 | 127 | 35 | April 28 2010
1038 | 127 | 35 | April 30 2010
1037 | NULL | NULL | May 1 2010
1038 | 126 | 34 | May 21 2010
1037 | NULL | NULL | May 24 2010
The dates are regular timestamps in the database; I just simplified them here.
I need to get a list of the users for whom latitude and longitude are always null. So in the above example, that would be user 1037--user 1035 has one row with lat/lon information, and 1038 has two rows with lat/lon information, whereas for user 1037, in both rows the information is null.
What query can I use to achieve this result?
select distinct user_id
from table_name t
where not exists(
select 1 from table_name t1
where t.user_id = t1.user_id and
t1.latitude is not null and
t1.longitude is not null
)
You can read this query: give me all users that haven't set lat and long different than null in any row in table. In my opinion exists is preferred in such case (no exists) because even if table scan is used (not optimal way to find row) it stops just after it finds specific row (there is no need to count all rows).
Read more about this topic: Exists Vs. Count(*) - The battle never ends... .
Try this, it should work.
SELECT user_id, count(latitude), count(longitude)
FROM user_loc
GROUP BY user_id HAVING count(latitude)=0 AND count(longitude)=0;
tested in MySQL.
Try:
SELECT * FROM user WHERE latitude IS NULL AND longitude IS NULL;
-- Edit --
2nd try (untested, but constructed it from a query I have used before):
SELECT user_id, CASE WHEN MIN(latitude) IS NULL AND MAX(latitude) IS NULL THEN 1 ELSE 0 END AS noLatLong FROM user GROUP BY user_id HAVING noLatLong = 1;
This works:
SELECT DISTINCT user_id
FROM table
WHERE latitude IS NULL
AND longitude IS NULL
AND NOT user_id IN
(SELECT DISTINCT user_id
FROM table
WHERE NOT latitude IS NULL
AND NOT longitude IS NULL)
result:
1037
(syntax validated with SQLite here)
BUT: Even if not using COUNT here, my statement has to scan all table lines, so MichaƂ Powaga's statement is more efficient.
rationale:
get list of user_ids with lat/lon records to compare against (you want to EXCLUDE these from final result) - optimization: use EXISTS here...
get list of user_ids without lat/lon records (that you're interested in)
reduce by all IDs, that exist in the first list - optimization: use EXISTS here...
make user_ids DISTINCT, because the example shows multiple entries per user_id (but you want just the unique IDs)