SQL Group By - sql

If I have a set of records
name amount Code
Dave 2 1234
Dave 3 1234
Daves 4 1234
I want this to group based on Code & Name, but the last Row has a typo in the name, so this wont group.
What would be the best way to group these as:
Dave/Daves 9 1234

As a general rule if the data is wrong you should fix the data.
However if you want to do the report anyway you could come up with another criteria to group on, for example LEFT(Name, 4) would perform a grouping on the first 4 characters of the name.
You may also want to consider the CASE statement as a method (CASE WHEN name = 'Daves' THEN 'Dave' ELSE name), but I really don't like this method, especially if you are proposing to use this for anything else then a one-off report.

If it's a workaround, try
SELECT cname, SUM(amount)
FROM (
SELECT CASE WHEN NAME = 'Daves' THEN 'Dave' ELSE name END AS cname, amount
FROM mytable
)
GROUP BY cname
This if course will handle only this exact case.

For MySQL:
select
group_concat(distinct name separator '/'),
sum(amount),
code
from
T
group by
code
For MSSQL 2005+ group_concat() can be implemented as .NET custom aggregate.

Fix the typo? Otherwise grouping on the name is going to create a new group.
Fixing your data should be your highest priority instead of trying to devise ways to "work around" it.
It should also be noted that if you have this single typo in your data, it is likely that you have (or will have at some point in the future) even more screwy data that will not cleanly fit into your code, which will force you to invent more and more "work arounds" to deal with it, when you should be focusing on the cleanliness of your data.

If the name field is suppose to be a key then the assumption has to be that Dave and Daves are two different items all together, and thus should be grouped differently. If however it is a typo, then as other have suggested, fix the data.
Grouping on a freeform entered text field if that is what this is, will always have issues. Data entry is never 100%.
To me it makes more sense to group on the code alone if that is the key field and leave name out of the grouping all together.

Related

How do I increase a counter by 1 for each repeated value?

As part of my course in university I have to make a database in Microsoft Access which is somewhat limiting me on what I'm trying to do. I have a table that has the information of whether a player in a team was present for a fixture or not using the values "P", "R", and "M" (Played, Reserves, Missed). I want to make a query that counts a value of 1 for each value of P or R and a separate one for M, so that when I make a report that prints off a membership card, it shows the amount of fixtures they've played in and the amount of fixtures that they have missed.
Sorry if this isn't clear, I'll try to explain myself further if you ask but I'm not very good with this stuff. Thank you.
Edit: I'll use screenshot links if that's okay, here is the Fixture Attendance entity that shows if a member of a team attended a game or not. I'm making a membership card based off this one. I want to be able to display the No. of fixtures played by the member and the No. of fixtures missed based off the values in the above entity and use that information in a form I'm going to create. That will be a subform inside my Membership Card form.
I'm presumably really bad at explaining this - I understand Access is rarely used in the real world so I'm not sure why I'm doing this in the first place and don't feel like I'm getting any real knowledge of working with databases.
You should use the COUNT function.
http://office.microsoft.com/en-us/access-help/count-data-by-using-a-query-HA010096311.aspx
I am guessing that you want something like this:
select playerid, sum(iif(fixture in ("P", "R"), 1, 0)) as NumPR,
sum(iif(figure = "M", 1, 0)as NumM
from table t
group by playerid;
The key idea here is putting the conditional part (iif()) inside the sum().
CASE WHEN can be used to translate the codes into 1's and 0's. Then use SUM with a GROUP BY to sum them.
SELECT player_id, SUM(Played), SUM(Reserve), SUM(Missed)
FROM
(SELECT player_id,
CASE WHEN present = 'P' THEN 1 ELSE 0 AS Played,
CASE WHEN present = 'R' THEN 1 ELSE 0 AS Reserve,
CASE WHEN present = 'M' THEN 1 ELSE 0 AS Missed
FROM fixtures)
GROUP BY player_id;

Access 2010 conditional selection in query

I'm trying to complete my university coursework but i'm stuck with the selection process in a query.
Running Access 2010, I have to select the following fields:
Asset ID, Asset Description, Problem ID, Current Helpdesk person allocated to the problem or the date solved and the solution given.
How can I do a conditional select so that basically like it says, IF solution date is filled in, fetch the date and description. Otherwize, fetch the Helpdesk person who is assigned.
Please assume that all fields are in the Problems table and I can adapt from there as needed, just need a heads up on how to select a column in one case and if the case resolves to false, select an alternative column.
I have not much idea about MS Access, but you can try it with IIF() function like below as MS Access doesn't support CASE WHEN clause. You can also use Format() function to convert date in string with whatever format you want.
SELECT Asset_ID,
Asset_Description,
Problem_ID
IIF(IsNull(Date), Helpdesk_Person, CStr(Date) + Description) as Solution
FROM Problems

SQL Group on a combination of values

I'm working a DB design regarding how a user launched something.
My idea was to have timestamp (DateTime) column, and a method column (varchar).
This 'method' (varchar) could be anything:
BUTTON_OK
BUTTON_X
APP_Y
APP_Z
etc
How can I COUNT the uses but group some values. In this case I want to have my result:
BUTTONS: 20
APP_X: 10
APP_Z: 14
You need some way of defining which 'methods' fall into which 'method group'.
One way would be to have a lookup table:
tbl_methodgroup
method_id Method Method_group
1 Button_OK Buttons
2 Button_X Buttons
3 App_Y App_Y
4 App_Z App_Z
then you could use:
select
b.method_group,
count(1)
from
tbl_methodgroup a
inner join tbl_method b on a.Method=b.Method
group by b.method_group
This method would have the advantage of being scalable as more methods get added. Rather than hand coding queries, which would need to be modified each time.
If the name of the table is tblTest, then the query will look like following:
SELECT method, COUNT(*) FROM tblTEst Group BY method
Apologies if I missread question, last chance to make it right if you have consistency in the data and grouping scenarios you can do following:
SELECT LEFT(method,CHARINDEX('_',method)-1),
COUNT(*)
FROM tblTest
GROUP BY LEFT(method,CHARINDEX('_',method)-1)
Otherwise Stuart Moore's answer is correct one.

Custom SQL sort by

Use:
The user searches for a partial postcode such as 'RG20' which should then be displayed in a specific order. The query uses the MATCH AGAINST method in boolean mode where an example of the postcode in the database would be 'RG20 7TT' so it is able to find it.
At the same time it also matches against a list of other postcodes which are in it's radius (which is a separate query).
I can't seem to find a way to order by a partial match, e.g.:
ORDER BY FIELD(postcode, 'RG20', 'RG14', 'RG18','RG17','RG28','OX12','OX11')
DESC, city DESC
Because it's not specifically looking for RG20 7TT, I don't think it can make a partial match.
I have tried SUBSTR (postcode, -4) and looked into left and right, but I haven't had any success using 'by field' and could not find another route...
Sorry this is a bit long winded, but I'm in a bit of a bind.
A UK postcode splits into 2 parts, the last section always being 3 characters and within my database there is a space between the two if that helps at all.
Although there is a DESC after the postcodes, I do need them to display in THAT particular order (RG20, RG14 then RG18 etc..) I'm unsure if specifying descending will remove the ordering or not
Order By Case
When postcode Like 'RG20%' Then 1
When postcode Like 'RG14%' Then 2
When postcode Like 'RG18%' Then 3
When postcode Like 'RG17%' Then 4
When postcode Like 'RG28%' Then 5
When postcode Like 'OX12%' Then 6
When postcode Like 'OX11%' Then 7
Else 99
End Asc
, City Desc
You're on the right track, trimming the field down to its first four characters:
ORDER BY FIELD(LEFT(postcode, 4), 'RG20', 'RG14', ...),
-- or SUBSTRING(postcode FROM 1 FOR 4)
-- or SUBSTR(postcode, 1, 4)
Here you don't want DESC.
(If your result set contains postcodes whose prefixes do not appear in your FIELD() ordering list, you'll have a bit more work to do, since those records will otherwise appear before any explicitly ordered records you specify. Before 'RG20' in the example above.)
If you want a completely custom sorting scheme, then I only see one way to do it...
Create a table to hold the values upon which to sort, and include a "sequence" or "sort_order" field. You can then join to this table and sort by the sequence field.
One note on the sequence field. It makes sense to create it as an int as... well, sequences are often ints :)
If there is any possibility of changing the sort order, you may want to consider making it alpha numeric... It is a lot easier to insert "5A" between "5 and "6" than it is to insert a number into a sequence of integers.
Another method I use is utilising the charindex function:
order by charindex(substr(postcode,4,1),"RG20RG14RG18...",1)
I think that's the syntax anyway, I'm just doing this in SAS at the moment so I've had to adapt from memory!
But essentially the sooner you hit your desired part of the string, the higher the rank.
If you're trying to rank on a large variety of postcodes then a case statement gets pretty hefty.

Sorting SQL by first two characters of fields

I'm trying to sort some data by sales person initials, and the sales rep field is 3 chars long, and is Firstname, Lastname and Account type. So, Bob Smith would be BS* and I just need to sort by the first two characters.
How can I pull all data for a certain rep, where the first two characters of the field equals BS?
In some databases you can actually do
select * from SalesRep order by substring(SalesRepID, 1, 2)
Othere require you to
select *, Substring(SalesRepID, 1, 2) as foo from SalesRep order by foo
And in still others, you can't do it at all (but will have to sort your output in program code after you get it from the database).
Addition: If you actually want just the data for one sales rep, do as the others suggest. Otherwise, either you want to sort by the thing or maybe group by the thing.
What about this
SELECT * FROM SalesTable WHERE SalesRepField LIKE 'BS_'
I hope that you never end up with two sales reps who happen to have the same initials.
Also, sorting and filtering are two completely different things. You talk about sorting in the question title and first paragraph, but your question is about filtering. Since you can just ORDER BY on the field and it will use the first two characters anyway, I'll give you an answer for the filtering part.
You don't mention your RDBMS, but this will work in any product:
SELECT
my_columns
FROM
My_Table
WHERE
sales_rep LIKE 'BS%'
If you're using a variable/parameter then:
SELECT
my_columns
FROM
My_Table
WHERE
sales_rep LIKE #my_param + '%'
You can also use:
LEFT(sales_rep, 2) = 'BS'
I would stay away from:
SUBSTRING(sales_rep, 1, 2) = 'BS'
Depending on your SQL engine, it might not be smart enough to realize that it can use an index on the last one.
You haven't said what DBMS you are using. The following would work in Oracle, and something like them in most other DBMSs
1) where sales_rep like 'BS%'
2) where substr(sales_rep,1,2) = 'BS'
SELECT * FROM SalesRep
WHERE SUBSTRING(SalesRepID, 1, 2) = 'BS'
You didn't say what database you were using, this works in MS SQL Server.