Hi guys I hope you can help me with a SQL query I'm trying to write. I have achieved the table I want but I have to build it up row by row. This is not efficient and when I end up with 200 rows this is slowing the website down.
This is what my Table looks like:
FeedBack Table:
RatingID | RATING| TUTORIAL_ID| TUTORIAL_NAME
5716| 4 | 993| Test002
5717| 3 | 993| Test002
5777| 1 | 994| eClip3
5886| 1 | 994| eClip3
7127| 4 | 1235| FTIR001
7128| 4 | 1235| FTIR001
7169| 3 | 1235| FTIR001
7170| 2 | 1235| FTIR001
7131| 4 | 1235| FTIR001
7187| 3 | 1235| FTIR001
7132| 3 | 1235| FTIR001
What I wanted to produce was a table of all the unique tutorial names and then the Total number of times that specific tutorial was rated;
1(not useful), 2(somewhat useful), 3(useful), 4(Very Useful)
So The query should make:
Tutorial Name | not Useful | Somewhat Useful| Useful| Very Useful
Test002| 0| 0|1|1
eClip3|2|0|0|0
FTIR001| 0| 1| 3| 3
The table is being shown on a webpage with C# behind. Currently I loop through the table to find a list of individual tutorial names then for each clip name I select Count(*) where rating = 1 etc.. and build the table up row by row.
What I really want to know is if there is a way to do all of this at once. This would imporve the efficiency of the website greatly as there are 200+ tutorials I want to show data for.
select tutorial_name,
sum(case when rating=1 then 1 else 0 end) "not Useful",
sum(case when rating=2 then 1 else 0 end) "Somewhat Useful",
sum(case when rating=3 then 1 else 0 end) "Useful",
sum(case when rating=4 then 1 else 0 end) "Very Useful"
from feedback
group by tutorial_name;
is the query you need.
Related
I have a table looking as follows: (with a few more irrelevant rows)
| user_id | user_name | fk_role_id| password|
| 1 | us1 | 1 | 1234 |
| 2 | us2 | 2 | 1234 |
| 3 | us3 | 2 | 1234 |
| 4 | us4 | 4 | 1234 |
I need to form/create an SQL statement that is counting the amount of entries with the fk_role_id of 1.
If there is more than one user with that fk_role_id, it can delete that user, but if there is only one user with fk_role_id it will fail, or give an error message stating that, that user is the last one with that fk_role_id and therefore it can't be deleted.
So far I have not found anything anywhere near that, that works. So hopefully someone in here will be able to help me quickly.
SQL server (2008 onwards):
with CTE as
(
select MT.*, row_number() over(partition by fk_role_id order by user_id) as rn
from MyTable MT
)
delete
from CTE
where rn >1
please try this statement.
DELETE TOP (1) FROM table_name WHERE fk_role_id= (SELECT fk_role_id
FROM table_name GROUP BY fk_role_id HAVING COUNT(fk_role_id)>1)
Each time the statement is executed it deletes the top row till last only one record is left. For the last record, Having condition fails and hence it will not be deleted from your table.
I am struggling with a simple update statement in Oracle. The update itself has not changed in forever but the table has grown massively and the performance is now unacceptable.
Here is the low down:
70 columns
27 indexes (which I am not under any circumstances allowed to reduce)
50M rows
Update statement is just hitting one table.
Update statement:
update TABLE_NAME
set NAME = 'User input string',
NO = NO,
PLANNED_START_DATE = TO_DATE('3/2/2016','dd/mm/yyyy'),
PLANNED_END_DATE = TO_DATE('3/2/2016','dd/mm/yyyy'),
WHERE ID = 999999 /*pk on the table*/
Execution Plan:
==================
Plan hash value: 2165476569
-----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------
| 0 | UPDATE STATEMENT | | 1 | 245 | 1 (0)| 00:00:01 |
| 1 | UPDATE | TABLE_NAME | | | | |
| 2 | TABLE ACCESS BY INDEX ROWID| TABLE_NAME | 1 | 245 | 1 (0)| 00:00:01 |
|* 3 | INDEX UNIQUE SCAN | PK_INDEX | 1 | | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("ID"=35133238)
==================================================
The update statement originates in a C# application but I am free to change the statement there.
Select statements still perform well thanks to all the indexes but as I see it that is exactly what is wrong with the update - it has to go update all the indexes.
We are licensed for partitioning but this table is NOT partitioned.
How can I improve the performance of this update statement without altering the table or its indexes?
Are you sure that column id is primary key? And is primary key based on unique index? Because in this case CBO would use INDEX UNIQUE SCAN. In your plan CBO expected 188 rows using filter ID (primary kay) = value and uses INDEX RANGE SCAN
I've spent a bit of time looking at this and cannot find an answer online (Perhaps I am searching for the wrong thing..)
Below is a simplified version of the problem.
I have two tables:
Table 1 : Areas
AreaID | Group 1 | Group2 | Group3
----------------------------------------------
1 | 2 | 22 | 10
2 | 5 | 1 | 9
3 | 4 | 3 | 2
Table 2 : Groups
GroupID | Group | Code | Description
-------------------------------------------------
1 | 1 | 2 | Description 1
2 | 1 | 5 | Description 2
3 | 1 | 4 | Description 3
4 | 2 | 22 | Description 4
5 | 2 | 1 | Description 5
6 | 2 | 3 | Description 6
7 | 3 | 10 | Description 7
8 | 3 | 9 | Description 8
9 | 3 | 2 | Description 9
So the SQL to get the Group description for Area 1 Group 3 would be:
Select g.Description from Areas a
inner join Groups g on g.Code = a.Group3 and g.Group = 3
where a.AreaID = 1
To clarify the Areas table has one Foreign Key linking it to the Groups table, but to get a unique record from the groups table you also need to have the "Group" column.
This is fine using ADO.Net or Stored procs, but we really would like to use EF and be able to navigate between the entities properly.
I also need to point out that for the purposes of this project we ONLY need the Group3 from the areas table, we are not interested in any other groupings at the moment.
Where I am upto:
I have created classes to represent the tables in my project, I have added modelbindings to the context to define a relationship between the Area and the Group based on the Area.Group3 column mapping to the Group.Code column and this works (Its essentially a many to many relationship in EF at the moment) but it also brings out all of the rows for other groups where the Code column matches (Such as code 2 in the example above brings back GroupIDs 1 and 9)
What I would like to be able to do is constrain it in the context by saying something like
modelBuilder.Entity<Area>()
.hasRequired(a=>a.Group)
.WithMany(g=>g.Areas)
.map(m=>
{m.MapKey("Group3")});
but of course the above without a constraint on Group.Group = 3 brings back multiple groups for each Area which in turn breaks because the binding is telling it to expect one!
Thats a bit of a ramble, if anyone needs clarification on the above to be able to help let me know and Ill get the info to you!
Thanks
I have a SQL Server database that will contain many tables that all connect, each with a primary key. I have a Dictionary that keeps track of the the primary keys fields are for each table. My task is to extract data every day from attribute-centric XML files and insert them into a master database. Each XML file has the same schema. I'm doing this by using an XMLReader and importing the data into a DataSet.
I can't use an AutoNumber for the keys. Let's say yesterday's XML file produced a DataTable similar to the following, and it was imported into a database
-------------------------------------
| Key | Column1 | Column2 | Column3 |
|-----------------------------------|
| 0 | dsfsfsd | sdfsrer | sdfsfsf |
|-----------------------------------|
| 1 | dertert | qweqweq | xczxsdf |
|-----------------------------------|
| 2 | prwersd | xzcsdfw | qwefkgs |
-------------------------------------
If today's XML file produces the following DataTable
-------------------------------------
| Key | Column1 | Column2 | Column3 |
|-----------------------------------|
| 0 | sesdfsd | hjghjgh | edrgffb |
|-----------------------------------|
| 1 | wrwerwr | zxcxfsd | pijghjh |
|-----------------------------------|
| 2 | vcbcvbv | vbnvnbn | bnvfgnf |
-------------------------------------
Then when I go to import the new data into the database using SqlBulkCopy, then there will be duplicate keys. My solution to this is to use DateTime.Now.Ticks to generate unique keys. Theoretically, this should always create a unique key.
However, for some reason DateTime.Now.Ticks is not unique. For example, 5 records in a row might all have the key 635387859864435908, and the next 7 records might have the key 635387859864592164, even though I am generating that value at different times. I want to say that the cause of the problem is that my script is calling DateTime.Now.Ticks several times before it updates the time.
Can anyone else think of a better way to generate keys?
It's possible that the value of DateTime.Now is cached for a small amount of time for performance reasons. We do something similar to this and there are 2 possible options that we use:
Keep a list of numbers that you've used on the server you're on and increment if you can determine the number has already been used
Convert the field to a string and append a GUID or some other random identifier on the end of it. A GUID can be created with System.Guid.NewGuid().ToString();
Obviously neither of these plans are going to make the risk of collision zero, but they can help in reducing it.
If you have huge amount of data and you need to have a unique key for each row just use GUID
You could do something like the following to get a unique id (SQL Fiddle):
SELECT
CONCAT(YEAR(GETDATE()), DATEDIFF(DAY, STR(YEAR(GETDATE()), 4) + '0101',
GETDATE() ) + 1, ROW_NUMBER() OVER(ORDER BY id DESC)) UniqueID
FROM supportContacts s
This would work if you only run the query once per day. If you ran it more than once per day you would need to grab the seconds or something else (SQL Fiddle):
SELECT CONCAT(CurrYear, CurrJulian, CurrSeconds, Row) AS UniqueID
FROM
(
SELECT
YEAR(GETDATE()) AS CurrYear,
DATEDIFF(DAY, STR(YEAR(GETDATE()), 4) + '0101', GETDATE() ) + 1 AS CurrJulian,
ROW_NUMBER() OVER(ORDER BY id DESC) AS Row,
datediff(second, left(convert(varchar(20), getdate(), 126), 10), getdate()) AS CurrSeconds
from supportContacts s
) AS m
Preamble
I have been investigating a concept and what I am posting below is a cut down version of what I have been trying. If you look at it and think "That doesn't make any sense to do it that way" then it is probably because I doesn't make any sense - there may be more efficient ways of doing this. I just wanted to try this out because it looks interesting.
What I am attempting to do it to calculate arbitrary calculations using CLR custom aggregations in SQL using a Reverse-Polish-like implementation. I'm using the data:
K | Amt | Instruction | Order
--+-----+-------------+------
A | 100 | Push | 1
A | 1 | Multiply | 2
A | 10 | Push | 3
A | 2 | Multiply | 4
A | | Add | 5
A | 1 | Push | 6
A | 3 | Multiply | 7
A | | Add | 8
The result of the calculation should be 123 ( = (100 * 1) + (10 * 2) + (1 * 3) ).
Using the following SQL (and the CLR functions ReversePolishAggregate and ToReversePolishArguments* that I have written) I can get the correct result:
SELECT K
, dbo.ReversePolishAggregate(dbo.ToReversePolishArguments(Instruction, Amt))
FROM dbo.ReversePolishCalculation
GROUP BY K;
The Problem
I want to generalise the solution more by putting the instructions and order in a separate table so that I can create calculations on arbitrary data. For example, I was thinking of a table like this:
Item | Type | Amount
-----+----------+-------
A | Budgeted | 10
A | Actual | 12
B | Actual | 20
B | Budgeted | 18
and joining it to a calculation table like this
Type | Instruction | Order
---------+-------------+------
Budgeted | Push | 1
Actual | Minus | 2
to calculated whether each item is over or under budget. The important consideration is that minus is non-commutative so I need to specify the order to ensure that the actual amount is subtracted from the budgeted amount, not the other way around. I expected that I would be able to do this with the ORDER BY clause inside the OVER clause of the aggregation (and then a little more tweaking that result).
SELECT K
, dbo.[ReversePolishAggregate](
dbo.[ToReversePolishArguments](Instruction, Amt))
OVER (PARTITION BY K ORDER by [Order])
FROM dbo.ReversePolishCalculation;
However I get the error:
Incorrect syntax near the keyword 'ORDER'.
I have checked the syntax by running the following SQL statement
SELECT K
, SUM(Amt) OVER (PARTITION BY K ORDER BY [Order])
FROM dbo.ReversePolishCalculation;
This works fine (it parses and runs, although I'm not sure that the result is meaningful), so I am left assuming that this is a problem with custom CLR aggregations or functions.
My Questions Is this supposed to work? Is there any documentation saying explicitly that this is not supported? Have I got the syntax right?
I'm using Visual Studio 2012 Premium, SQL Server Management Studio 2012 and .NET framework 4.0.
* I created 2 CLR functions as a work-around to not being able to pass multiple arguments into a single aggregation function - see this article.
EDIT: This post looks like it is not supported, but I was hoping for something a little more official.
As an answer:
Officially from Microsoft, No.
http://connect.microsoft.com/SQLServer/feedback/details/611026/add-support-for-over-order-by-for-clr-aggregate-functions
It doesn't appear to have made it into 2014 (or 2016 CTP 3) ... no mentions of many transact-sql changes:
http://msdn.microsoft.com/en-us/library/bb510411.aspx#TSQL