I'm having trouble with SQLite query optimization, it runs fine, but for large tables it takes too much time and I need some help with optimizing it.
Source table:
-------+----------+----------------
IdMain | IdParent | ColumnToUpdate
-------+----------+----------------
1 | |
2 | 1 | 999 <-- IdParent = 1 \
3 | | \
4 | 5 | 123 > DISTINCT ITEMS COUNT = 1
5 | | / IdParent = 1
6 | 1 | 999 <-- IdParent = 1 / UPDATE Row with IdMain = IdParent
7 | 4 |
8 | 3 | 456
-------+----------+----------------
Query to optimize
UPDATE Table
SET ColumnToUpdate = (SELECT DISTINCT ColumnToUpdate
FROM Table T
WHERE T.ColumnToUpdate IS NOT NULL
AND T.IdParent = Table.IdMain)
WHERE Table.ColumnToUpdate IS NULL
AND (SELECT COUNT(*) FROM (SELECT DISTINCT ColumnToUpdate
FROM Table T2
WHERE T2.ColumnToUpdate IS NOT NULL
AND T2.IdParent = Table.IdMain)) = 1 ;
Expected table
-------+----------+----------------
IdMain | IdParent | ColumnToUpdate
-------+----------+----------------
1 | | 999 <-- UPDATE
2 | 1 | 999
3 | |
4 | 5 | 123
5 | |
6 | 1 | 999
7 | 4 |
8 | 3 | 456
-------+----------+----------------
Pseudo algorithm
FOR Row DO
BEGIN
IF ColumnToUpdate = NULL THEN
BEGIN
// count distinct values in ColumnToUpdate
X = COUNT(DISTINCT(ColumnToUpdate(WITH IdParent = IdMain))
// update row ONLY when number of distinct count equals = 1
IF X = 1 THEN
UPDATE(ColumnToUpdate)
END
END
I've tried to split it up within the source code (currently Delphi) but it works slow too. Is there any way to speed it up?
I wonder if this might speed things up:
UPDATE Table
SET ColumnToUpdate = coalesce((SELECT ColumnToUpdate
FROM Table T
WHERE T.ColumnToUpdate IS NOT NULL AND
T.IdParent = Table.IdMain
GROUP BY ColumnToUpdate
HAVING count(*) = 1),
Table.ColumnToUpdate
)
WHERE Table.ColumnToUpdate IS NULL;
This only executes the subquery once instead of twice.
Also, an index on Table(IdParent, ColumnToUpdate) might also improve performance.
Related
I have a table Table1:
ID | RefID | Answer | Points |
----+-------+---------+--------+
1 | 1 | 1 | 5 |
2 | 1 | 2 | 0 |
3 | 1 | 3 | 3 |
4 | 2 | 1 | 4 |
I have a result set in temp table Temp1 with same structure and have update Table1 only if for refID answer and points have changed, otherwise there should be deletion for this record.
I tried:
update table1
set table1.answer = temp1.answer,
table1.points = temp1.points
from table1
join temp1 on table1.refid = temp1.refid
where table1.answer != temp1.answer or table1.points != temp1.points
Here is a fiddle http://sqlfiddle.com/#!18/60424/1/1
However this is not working and don't know how to add the delete condition.
Desired result should be if tables not the same ex. (second row answer 2 points3):
ID | RefID | Answer | Points |
----+-------+---------+--------+
1 | 1 | 1 | 5 |
2 | 1 | 2 | 3 |
3 | 1 | 3 | 3 |
4 | 2 | 1 | 4 |
if they are same all records with refID are deleted.
Explanation when temp1 has this data
ID | RefID | Answer | Points |
----+-------+---------+--------+
12 | 1 | 1 | 5 |
13 | 1 | 2 | 0 |
14 | 1 | 3 | 3 |
EDIT: adding another id column questionid solved the update by adding this also in join.
Table structure is now:
ID | RefID | Qid |Answer | Points |
----+-------+------+-------+--------+
1 | 1 | 10 | 1 | 5 |
2 | 1 | 11 | 2 | 0 |
3 | 1 | 12 | 3 | 3 |
4 | 2 | 11 | 1 | 4 |
SQL for update is: (fiddle http://sqlfiddle.com/#!18/00f87/1/1) :
update table1
set table1.answer = temp1.answer,
table1.points = temp1.points
from table1
join temp1 on table1.refid = temp1.refid and table1.qid = temp1.qid
where table1.answer != temp1.answer or table1.points != temp1.points;
SELECT ID, refid, answer, points
FROM table1
How can I make the deletion case, if data is same ?
You need to add one more condition in the join to exactly match the column.Try this one.
update table1
set table1.answer=temp1.answer,
table1.points=temp1.points
from
table1 join temp1 on table1.refid=temp1.refid and **table1.ID=temp1.ID**
where table1.answer!=temp1.answer or table1.points!=temp1.points
I would first do the delete, and only then the update.
The reason for this is that once you've deleted all the records where the three columns are the same, your update statement becomes simpler - you only need the join, and no where clause:
DELETE t1
FROM table1 AS t1
JOIN temp1 ON t1.refid = temp1.refid
AND t1.qid = temp1.qid
AND t1.answer=temp1.answer
AND t1.points=temp1.points
UPDATE t1
SET answer = temp1.answer,
points = temp1.points
FROM table1 AS t1
JOIN temp1 ON t1.refid=temp1.refid
AND t1.qid = temp1.qid
I think from what i understood that you need to use id instead of refid or both if id is unique
I have database with this type of records:
Id | Value | DocId |
------ | ------ | ------|
1 | 10 | null |
2 | -10 | 1 | //this is child of record with id = 1
3 | 15 | null |
4 | -15 | 3 | //this is child of record with id = 3
5 | 7 | null |
6 | -7 | 5 | //this is child of record with id = 5
7 | 16 | null |
So I want to select the records where Id = 1 and Id = DocId, so this should return (because those are the records with Id = 1 and DocId = 1)
Id | Value | DocId |
------ | ------ | ------|
1 | 10 | null |
2 | -10 | 1 |
I know I can use a where clause but I need to do it with Join.
You can select twice from the same table as if they were two separate tables:
select * from TheTable t1, TheTable t2 where t1.id = t2.DocId;
You are showing a result for where id = 1 or docid = 1. So why must you use a join instead? This doesn't seem to make sense. Anyway, here you go:
select t.*
from t
join (select 1 as id) x on x.id in (t.id, t.docid);
You are saying you want the records where the id = 1 or the docId = 1. So in sql you can say almost exactly that:
select * from my_table where id = 1 or docId = 1;
Obviously there are a bunch of questions about ROW_NUMBER in MS Access and the usually response is that it does not exist but instead to use a COUNT(*) to create something similar. Unfortunately, doing so does not give me the results that I need.
My data looks like:
RID | QID
---------
1 | 1
1 | 2
1 | 3
1 | 3
2 | 1
2 | 2
2 | 2
What I am trying to get at is a unique count over RID and QID so that my query output looks like
RID | QID | SeqID
------------------
1 | 1 | 1
1 | 2 | 1
1 | 3 | 1
1 | 3 | 2
2 | 1 | 1
2 | 2 | 1
2 | 2 | 2
Using the COUNT(*) I get:
RID | QID | SeqID
------------------
1 | 1 | 1
1 | 2 | 2
1 | 3 | 3
1 | 3 | 3
2 | 1 | 1
2 | 2 | 2
2 | 2 | 2
My current query is:
SELECT
d.RID
,d.QID
,(SELECT
COUNT(*)
FROM
Data as d2
WHERE
d2.RID = d.RID
AND d2.QID < d.QID) + 1 AS SeqID
FROM
Data as d
ORDER BY
d.RID
,d.QID
Any help would be greatly appreciated.
As Matt's comment implied, the only way to make this work is if you have some column in your table that can uniquely identify each row.
Based on what you have posted, you don't seem to have that. If that's the case, consider adding a new auto increment numeric column that can serve that purpose. Let's pretend that you call that new column id.
With that in place, the following query will work:
select t.rid, t.qid,
(select count(*)
from data t2
where t2.rid = t.rid
and t2.qid = t.qid
and t2.id <= t.id) as SeqID
from data t
order by t.rid, t.qid
SQLFiddle Demo
Thank you for interested in question.
I have in one table some thing like this
sifrez | sifKorisnikPK | Status
1 | 1 | 'P'
2 | 1 | 'P'
3 | 1 | 'U'
4 | 2 | 'P'
5 | 2 | 'P'
6 | 2 | 'U'
7 | 2 | 'U'
8 | 3 | 'U'
9 | 3 | 'U'
10 | 3 | 'U'
11 | 3 | 'U'
12 | 4 | 'P'
13 | 4 | 'P'
Then i created stored function which count values of P and U search ratio between them find user with that id and return username
CREATE PROCEDURE sp_getBestUsernames
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
SELECT SifKorisnikPK, COUNT (Status) AS BrojLosih
INTO #LosaRez
FROM Rezervacija
WHERE Status = 'P'
GROUP BY SifKorisnikPK
order by count(*) desc
After this part it give me
sifKorisnikPK | BrojLosih
1 | 2
2 | 2
4 | 2
This only count p on status.
And this:
SELECT SifKorisnikPK, COUNT (Status) AS BrojDobrih
INTO #DobraRez
FROM Rezervacija
WHERE Status = 'U'
GROUP BY SifKorisnikPK
order by count(*) desc
i get
sifKorisnikPK | BrojDobrih
1 | 1
2 | 2
3 | 4
Only count U. This part work like a charm...
And here i try to join this two temp tables and calculate ratio and return SifKorisnik with best ratio
SELECT Username FROM Korisnik
WHERE SifKorisnik = (SELECT TOP 1 a.SifKorisnikPK
FROM #DobraRez a
INNER JOIN #LosaRez b
ON a.SifKorisnikPK = b.SifKorisnikPK OR
a.SifKorisnikPK != b.SifKorisnikPK
WHERE a.BrojDobrih - b.BrojLosih > 0
ORDER BY a.BrojDobrih - b.BrojLosih )
END
And after joining operation i expect to get some thing like this, which i'm not getting
sifKorisnikPK | BrojDobrih | BrojLosih
1 | 1 | 2
2 | 2 | 2
3 | 4 | 0
4 | 0 | 2
so i can calculate the ratio BrojDobrih - BrojLosih and return the SifKorisnikPK which is the best ratio. In this case it would be SifKorisnik 3.
But my procedure get SifKorisnik 2 which is second best. I presume that the problem is in joining operation that it doesn't input 0. So how can i solve this?
Try this:
SELECT
A.sifKorisnikPK,
IsNull(BrojDobrih,0) BrojDobrih,
IsNull(BrojLosih,0) BrojLosih
FROM (select distinct sifKorisnikPK from Rezervacija) A
LEFT JOIN #LosaRez B
ON A.sifKorisnikPK = B.sifKorisnikPK
LEFT JOIN #DobraRez C
ON A.sifKorisnikPK = C.sifKorisnikPK
ORDER BY (IsNull(BrojDobrih,0) - IsNull(BrojLosih,0))
Using SQL Server 2008, I want to query a table like so:
| ID | Number
-------------
| 1 | 0
| 2 | 0
| 3 | 1
| 4 | 0
| 5 | 0
| 6 | 1
| 7 | 1
| 8 | 1
The result should be the same table with an additional column that counts.
The method of counting is: if the number in "number" equals to 1 - increment the counter by one for the next line.
An example of result for the provided table:
| ID | Number | Counter
-----------------------
| 1 | 0 | 1
| 2 | 0 | 1
| 3 | 1 | 1
| 4 | 0 | 2
| 5 | 0 | 2
| 6 | 1 | 2
| 7 | 1 | 3
| 8 | 1 | 4
How can this be achieved?
select [ID], [Number],
isnull(1+(select sum([Number]) from Table1 t2 where t2.ID<t1.Id),1)
from Table1 t1
SQL Fiddle to test
This is not too hard to do. What you are looking for is very much like the running total, which you get with sum and a windowing clause.
select id, num, 1 + sum(num) over (order by id) - num as counter
from mytable
order by id;
Here is an SQL fiddle: http://sqlfiddle.com/#!4/958e2a/1.
You can use recursive select too but it is a bit complicated but if you insert other numbers which are greater than 1 it work fine:
with tab(id,number,counter,rn) as
(select t.*,1 as counter,1 as rn from table1 t where id = 1
union all
select t.*,case when t.number = 1 then counter + 1 else counter end as counter,
rn + 1 as rn from table1 t,tab where t.id = tab.rn + 1),
tab2 as (select id,number,counter from tab)
select id,number,case when number = 1 then counter - 1
else counter end as counter from tab2;
SQL Fiddle