Count redundant for each value in a column in SQL server - sql

If I have the following table in a SQL Server 2019 database as follows:
|id | name | count |
+-----+--------+--------+
| 1 | rose | 1 |
| 2 | peter | 1 |
| 3 | ann | 1 |
| 4 | rose | 2 |
| 5 | ann | 2 |
| 6 | ann | 3 |
| 7 | mike | 1 |
I would like to find out if an inserted name already exists in the column "name" and how many times and right a count next to it as shown in "count" column. For example when ann was inserted the second time I put count value bext to it which is 2, and ann was inserted the third time I put 3 next to it.
How to do that using SQL?
Thank you

Here is one approach using the insert ... select syntax:
insert into mytable (name, cnt)
select v.name, (select count(*) + 1 from mytable t where t.name = v.name)
from (values (#name)) v(name)
The value to insert is given as #name in derived table values(). Then we use a subquery to count how many such names already exist in the table.

I would suggest that you calculate this information when you query the table, rather than when you insert rows:
select t.*,
row_number() over (partition by name order by id) as count
from t;
The value will always be correct -- even when the data is updated or rows are deleted.

Select Name, Count(*)
From Table
Group By Name

Related

SQL sum all rows by id

I am trying to learn SQL queries and have this scenario where I have this table:
Table1
ID | Name | Hour
----------------
1 | Mark | 2
2 | ken | 1.5
3 | jake | 3
1 | Mark | 1.8
2 | ken | 1
Expected result
ID | Name | Hour
----------------
1 | Mark | 3.8
2 | ken | 2.5
3 | jake | 3
I have tried to use the sum() function but I get an error.
My query:
Select ID, Name, Sum(Hour)
From Table1
Where ID = ID
Response:
Kindly use Group by clause whenever the Aggregate functions (min(),max(),sum(),count(),...etc.,) and columns are used together.
Non aggregated columns present in SELECT columns should be used in GROUP BY clause.
For using aggregate function you need to use Group By like this:
Select ID, Name , Sum(Hour) AS Hour From Table1
Group By ID, Name
Order By ID

Replacing numeric values in a cloumn by their ranking respective the other values in the column

Lets say I have A table a that looks like this:
+---+--------------+------+
|NUM| NAME |POINTS|
+-------------------------+
| 1 | Peter | 92 |
| 1 | Rose | 93 |
| 1 | Karl | 94 |
| 2 | Frank | 15 |
| 2 | Sarah | 16 |
+-------------------------+
With the primary key being combination of NUM and NAME.
Now I would like to replace the numbers in POINTS with their ranking, starting by 1 for every num. I want to actually update the table.
Example:
+---+--------------+------+
|NUM| NAME |POINTS|
+-------------------------+
| 1 | Peter | 3 |
| 1 | Rose | 2 |
| 1 | Karl | 1 |
| 2 | Frank | 1 |
| 2 | Sarah | 2 |
+-------------------------+
What would be the best way of doing that?
If you want to actually change the values in the table, you can use a MERGE statement:
merge into the_table t
using (
select num, name,
dense_rank() over (partition by num order by points) as rnk
from the_table
) x on (x.num = t.num and x.name = t.name)
when matched then update
set points = x.rnk;
If you just want to display the values, use the inner select on its own:
select num, name,
dense_rank() over (partition by num order by points) as points,
from the_table
I would add a column to the table for ranking, then loop through the table while there exists a row with no ranking.
Then find the max points where ranking is null, and update ranking for all rows who have those points to their ranking.
The ranking is either a counter if the points are unique, or you could just count the records with ranking every time you loop and have the new ranking be count(records with ranking) + 1
All this would look something like this
FOR r in (SELECT * FROM people WHERE ranking IS NULL) LOOP
SELECT MAX(POINTS)
INTO temp_points
FROM people
WHERE ranking IS NULL;
SELECT COUNT(*)
INTO temp_ranking
FROM people
WHERE ranking is not null;
temp_ranking := temp_ranking + 1;
UPDATE people
SET ranking = temp_ranking
WHERE points = temp_points;
END LOOP;

Rows that have same value in a column, sum all values in another column and display 1 row

Example Table user:
ID | USER_ID | SCORE |
1 | 555 | 50 |
2 | 555 | 10 |
3 | 555 | 20 |
4 | 123 | 5 |
5 | 123 | 5 |
6 | 999 | 30 |
The result set should be like
ID | USER_ID | SCORE | COUNT |
1 | 555 | 80 | 3 |
2 | 123 | 10 | 2 |
3 | 999 | 30 | 1 |
Is it possible to generate a sql that can return the table above, so far I can only count the rows where certain user_id appear, but don't know how to sum and show for every user ?
You've included a column called "ID" in both the source data and desired results, but I'm going to assume that these ID values are not related and simply represent the row or line number - otherwise the question doesn't make sense.
In which case, you can simply use:
SELECT
USER_ID,
SUM(SCORE) AS SCORE,
COUNT(USER_ID) AS COUNT
FROM
<Table>
GROUP BY
USER_ID
If you really want to generate the ID column as well, then how you do this depends on the database platform being used. For example on Oracle you could use the ROWNUM pseudocolumn, on SQL Server you will need to use ROW_NUMBER() function (which also works for Oracle).
SELECT ID
,sum(SCORE)
,count(USER_ID)
FROM Table
GROUP BY
ID
I think COUNT is the number of scores per user_id, if so, then your sql request should be :
SELECT
ID,
USER_ID,
SUM(SCORE)AS SCORE,
COUNT(SCORE)AS COUNT
FROM
TABLE
GROUP BY
USER_ID

Selecting unique records from database

Running this query,
select * from table;
Returns the following
|branch | number |
-------------------
| 1 | 123 |
| 1 | 001 |
| 2 | 123 |
| 3 | 123 |
| 4 | 123 |
| 1 | 123 |
| 1 | 789 |
| 2 | 123 |
| 3 | 123 |
| 4 | 009 |
I want to find values that are unique to ONLY branch 1
| 1 | 001 |
| 1 | 789 |
Can this be done without the data being stored in separate tables? I've tried a few "select distinct" queries & don't seem to get the results I'm expecting.
SELECT branch, number
FROM table
WHERE branch = 1
GROUP BY branch, number
If you do not need any aggregates, you can use distinct instead of group by:
select distinct branch
, number
from YourTable
where branch = 1
I guess what I'm trying to say is that I want to find all numbers that are unique to ONLY branch 1. If they are found in any other branch, I don't want to see them.
I guess this is what you want.
SELECT distinct number
FROM MyTable
WHERE branch=1 and number not in
( SELECT distinct number
FROM MyTable
WHERE branch != 1 )
Try this:
SELECT branch, number
FROM table
GROUP BY branch, number
Here is a SQLFiddle for you to have a look at
If you want to limit it to only branch 1, then just add a where clause.
SELECT branch, number
FROM table
WHERE branch = 1
GROUP BY branch, number
To select all values that are unique in column number and have a branch value of 1 you can use the following code:
SELECT branch, number
FROM table1
WHERE number IN (
SELECT number
FROM table1
GROUP BY number
HAVING (COUNT(number ) = 1)
)
AND branch = 1
For a demo see http://sqlfiddle.com/#!2/97145/62

SQL distinct/groupby on combination of columns

I am trying to do a SQL select on a table based on two columns, but not in the usual way where the combination of values in both columns must be unique; I want to select where the value can only appear once in either column.
Given the dataset:
|pkid | fkself | otherData |
|-----+--------+-----------|
| 1 | 4 | there |
| 4 | 1 | will |
| 3 | 6 | be |
| 2 | 5 | other |
| 5 | 2 | data |
| 6 | 3 | columns |
I need to return either
|pkid | fkself | otherData |
|-----+--------+-----------|
| 1 | 4 | there |
| 3 | 6 | be |
| 2 | 5 | other |
or
|pkid | fkself | otherData |
|-----+--------+-----------|
| 4 | 1 | will |
| 5 | 2 | data |
| 6 | 3 | columns |
The only way I can think of to do this is to concatenate `pkid and fkid in order so that both row 1 and row 2 would concatenate to 1,4, but I'm not sure how to do that, or if it is even possible.
The rows will have other data columns, but it does not matter which row I get, only that I get each ID only once, whether the value is in pkid or fkself.
You can use least and greatest to get the smallest or biggest value of the two. That allows you to put them in the right order to generate those keys for you. You could concatenate the values as you suggested, but it's not needed in this solution. With dense_rank you can generate a sequence for each of those fictional keys. Then, you can get the first OtherData from that sequence.
select
pkid,
fkself,
otherData
from
(select
pkid,
fkself,
otherData,
dense_rank() over (partition by least(pkid, fkself), greatest(pkid, fkself) order by pkid) as rank
from
YourTable t)
where
rank = 1
Your idea is possible, and it should produce the results you want.
SELECT DISTINCT joinedID
FROM (
SELECT min(id) & "," & max(id) as joinedID
FROM (
SELECT pkid as id, someUniqueValue
FROM table
UNION ALL
SELECT fkself as id, someUniqueValue
FROM table)
GROUP BY someUniqueValue )
This will give you a unique list of IDs, concatenated as you like. You can easily include other fields by adding them to each SELECT statement. Also, someUniqueValue can be either an existing unique field, a new unique field, or the concatenated pkid and fkself, if that combination is unique.
The only way I can think of to do this is to concatenate `pkid and
fkid in order so that both row 1 and row 2 would concatenate to 1,4,
but I'm not sure how to do that, or if it is even possible.
You could do it using a CASE statement in Oracle:
SQL> SELECT * FROM sample
2 /
PKID FKSELF
---------- ----------
1 4
4 1
3 6
2 5
5 2
7 7
6 rows selected.
SQL> l
1 SELECT DISTINCT *
2 FROM (
3 SELECT CASE WHEN pkid <= fkself THEN pkid||','||fkself
4 ELSE fkself||','||pkid
5 END "JOINED"
6 FROM sample
7* )
SQL> /
JOINED
-------------------------------------------------------------------------------
1,4
2,5
3,6
7,7