Update statement on same table with multi column subsquery - sql

Everyone, I have been working on a query for last one hour that I need to sort country field alphabetically and update its sort_order field with incremantal numbers.
I wrote a select query which sorts my country field in alphabetic order and I added a row number. So far so good.
My select query response is :
objid | country | sort_order | my_rownum
--------------------------------------------
c1 | America | 0 | 1
g2 | Englanc | 0 | 2
k1 | France | 0 | 3
Now, I need to update my sort_order field with my_rownum values.
Country_Info Table :
objid | country | sort_order
----------------------------
c1 | America | 0
g2 | Englanc | 0
k1 | France | 0
After Update table must be like that :
objid | country | sort_order
----------------------------
c1 | America | 1
g2 | Englanc | 2
k1 | France | 3
I tried many sql queries but something is wrong with my queries.
Sample pseudo query for explaining what I need. I can not merge the logic.
-- sort_order will set when sample query objid and counrty_info objid are equals
update country_info
set sort_order = (value from subquery here)
where (value equivlance from subquery for objid here)
--Subquery
(select c.objid, c.country, c.sort_order, row_number() over (partition by 1 order by 1) as my_rownumber
from counrty c where <bla bla> order by c.country asc)
Regards...

Assuming you table name as table1, you can use merge clause like this:
MERGE INTO table1 t1
USING (SELECT s.*, ROWNUM my_rownum
FROM ( SELECT *
FROM table1
ORDER BY 3) s) t2
ON (T1.objid = t2.objid)
WHEN MATCHED
THEN
UPDATE SET T1.sort_order = t2.my_rownum
I hope this helps

if using SQL2005 or later, try:
select *, sort_order = dense_rank() over (order by country)
from yourtable
This would give all occurrences of the first country 1, all of the second 2, and so on. If you want unique sort values, use row_number().

Related

Get row which matched in each group

I am trying to make a sql query. I got some results from 2 tables below. Below results are good for me. Now I want those values which is present in each group. for example, A and B is present in each group(in each ID). so i want only A and B in result. and also i want make my query dynamic. Could anyone help?
| ID | Value |
|----|-------|
| 1 | A |
| 1 | B |
| 1 | C |
| 1 | D |
| 2 | A |
| 2 | B |
| 2 | C |
| 3 | A |
| 3 | B |
In the following query, I have placed your current query into a CTE for further use. We can try selecting those values for which every ID in your current result appears. This would imply that such values are associated with every ID.
WITH cte AS (
-- your current query
)
SELECT Value
FROM cte
GROUP BY Value
HAVING COUNT(DISTINCT ID) = (SELECT COUNT(DISTINCT ID) FROM cte);
Demo
The solution is simple - you can do this in two ways at least. Group by letters (Value), aggregate IDs with SUM or COUNT (distinct values in ID). Having that, choose those letters that have the value for SUM(ID) or COUNT(ID).
select Value from MyTable group by Value
having SUM(ID) = (SELECT SUM(DISTINCT ID) from MyTable)
select Value from MyTable group by Value
having COUNT(ID) = (SELECT COUNT(DISTINCT ID) from MyTable)
Use This
WITH CTE
AS
(
SELECT
Value,
Cnt = COUNT(DISTINCT ID)
FROM T1
GROUP BY Value
)
SELECT
Value
FROM CTE
WHERE Cnt = (SELECT COUNT(DISTINCT ID) FROM T1)

Creating sql view where the select is dependent on the value of two columns

I want to create a view in my database, based on these three tables:
I would like to select the rows in table3 that has the highest value in Weight, for rows that has the same value in Count.
Then I want them grouped by Category_ID and ordered by Date, so that if two rows in table3 are identical, I want the newest.
Let me give you an example:
Table1
ID | Date | UserId
1 | 2015-01-01 | 1
2 | 2015-01-02 | 1
Table2
ID | table1_ID | Category_ID
1 | 1 | 1
2 | 2 | 1
Table3
ID | table2_ID | Count | Weight
1 | 1 | 5 | 10
2 | 1 | 5 | 20 <-- count is 5 and weight is highest
3 | 1 | 3 | 40
4 | 2 | 5 | 10
5 | 2 | 3 | 40 <-- newest of the two equal rows
Then the result should be row 2 and 5 from table 3.
PS I'm doing this in mssql.
PPS I'm sory if the title is not appropriate, but I did not know how to formulate a good one.
SELECT
*
FROM
(
SELECT
t3.*
,RANK() OVER (PARTITION BY [Count] ORDER BY [Weight] DESC, Date DESC) highest
FROM TABLE3 t3
INNER JOIN TABLE2 t2 ON t2.Id = t3.Table2_Id
INNER JOIN TABLE1 t1 ON t1.Id = t2.Table1_Id
) t
WHERE t.Highest = 1
This will group by the Count (which must be the same). Then it will determine which has the highest weight. If two of more of them have the same 'heighest' weight, it takes the one with the most recent date first.
You can use RANK() analytic function here, and give those rows a rank and than choose the first rank for each ID
Something like
select *
from
(select
ID, table2_ID, Count, Weight,
RANK() OVER (PARTITION BY ID ORDER BY Count, Weight DESC) as Highest
from table3)
where Highest = 1;
This is the syntax for Oracle, if you not using it look in the internet for the your syntax which should be almost the same

Select the most common item for each category

Each row in my table belongs to some category, has some value and other data.
I would like to select each category with the most common value for it (doesn't matter which one if there are multiple), ordered by category.
some_table: expected result:
+--------+-----+--- +--------+-----+
|category|value|... |category|value|
+--------+-----+--- +--------+-----+
| 1 | a | | 1 | a |
| 1 | a | | 2 | b |
| 1 | b | | 3 | a # or b
| 2 | a | +--------+-----+
| 2 | b |
| 2 | c |
| 2 | b |
| 3 | a |
| 3 | a |
| 3 | b |
| 3 | b |
+--------+-----+---
I have a solution (posting it as an answer) but it seems suboptimal to me. So I'm looking for better solutions.
My table will have up to 10000 rows (possibly, but not likely, beyond that).
I'm planning to use SQLite but I'm not tied to it, so I may reconsider if SQLite can't do this with reasonable performance.
I would be inclined to do this using a correlated subquery:
select distinct category,
(select value
from some_table t2
where t2.category = t.category
group by value
order by count(*) desc
limit 1
) as mode_value
from some_table t;
The name for the most common value is "mode" in statistics.
And, if you had a categories table, this would be written as:
select category,
(select value
from some_table t2
where t2.category = c.category
group by value
order by count(*) desc
limit 1
) as mode_value
from categories c;
Here is one option, but I think it's slow...
SELECT DISTINCT `category` AS `the_category`, `value`
FROM `some_table`
WHERE `value`=(
SELECT `value`
FROM `some_table`
WHERE `category`=`the_category`
GROUP BY `value`
ORDER BY COUNT(`value`) DESC LIMIT 1)
ORDER BY `category`;
You can replace a part of this with WHERE `id`=( SELECT `id` if the table has a unique/primary key column, then the LIMIT 1 is not needed.
select category, value, count(*) value_count
from some_table t
group by category, value
order by category, value_count DESC;
returns us amout of each value in each category
select category, value
from (
select category, value, count(*) value_count
from some_table t
group by category, value) sub
group by category
actually we need the first value because it's sorted.
I am not sure sqlite leaves the first one and can't test but IMHO it should work

Oracle: DISTINCT or GROUP BY row consistency

I have following table:
Name Parent Status
A P1 0
A P2 1
B PB -1
Will following queue guarantee, that resulting data will be related to a single row:
SELECT
DISTINCT Name, Parent, Status
FROM
MyTable
For ex. could result set contain:
A, P1, 1
It doesn't match any row in the table. How can write an SQL statement, that selects ANY and AT MOST ONE row with each name?
SQL Fiddle
SELECT DISTINCT will get every row and then discard any duplicate rows in the result set.
The data you've given has duplicates in a column but no duplicate rows - so all rows will be returned:
Query 1:
SELECT DISTINCT
Name,
Parent,
Status
FROM MyTable
Results:
| NAME | PARENT | STATUS |
|------|--------|--------|
| A | P2 | 1 |
| B | PB | -1 |
| A | P1 | 0 |
For ex. could result set contain:
A, P1, 1
No, you can see from the above results that it does not. However, you can make a query that does:
Query 2:
SELECT Name,
MIN( Parent ),
MAX( Status )
FROM MyTable
GROUP BY Name
Results:
| NAME | MIN(PARENT) | MAX(STATUS) |
|------|-------------|-------------|
| A | P1 | 1 |
| B | PB | -1 |
In answer to your final question:
How can write an SQL statement, that selects ANY and AT MOST ONE row with each name?
This query orders the rows randomly and then selects the (randomly) first one for each name:
Query 3:
WITH Randomness AS (
SELECT Name,
Parent,
Status,
ROW_NUMBER() OVER ( PARTITION BY Name ORDER BY SYS.DBMS_RANDOM.VALUE() ) AS Random_ID
FROM MyTable
)
SELECT Name,
Parent,
Status
FROM Randomness
WHERE Random_ID = 1
Results:
| NAME | PARENT | STATUS |
|------|--------|--------|
| A | P1 | 0 |
| B | PB | -1 |
If you run Query 3 a second time then you may get the other A row returned (or not - it's random).
Or if you want to be rather silly and completely random then you can select a random parent and a random status for each name (such that the Parent and Status do not have to come from the same row of the original table).
Query 4:
SELECT Name,
MIN( Parent ) KEEP ( DENSE_RANK FIRST ORDER BY SYS.DBMS_RANDOM.VALUE() ) AS Random_Parent,
MIN( Status ) KEEP ( DENSE_RANK FIRST ORDER BY SYS.DBMS_RANDOM.VALUE() ) AS Random_Status
FROM MyTable
GROUP BY Name
Results:
| NAME | RANDOM_PARENT | RANDOM_STATUS |
|------|---------------|---------------|
| A | P1 | 1 |
| B | PB | -1 |
Please try:
SELECT
Name,
Parent,
Status
FROM(
select
Name,
Parent,
Status,
ROW_NUMBER()
OVER (PARTITION BY Name order by Status desc) RNum
From YourTable
)x where RNum=1
SQL Fiddle Demo

SQL - select distinct only on one column [duplicate]

This question already has answers here:
How can I SELECT rows with MAX(Column value), PARTITION by another column in MYSQL?
(22 answers)
Closed 9 years ago.
I have searched far and wide for an answer to this problem. I'm using a Microsoft SQL Server, suppose I have a table that looks like this:
+--------+---------+-------------+-------------+
| ID | NUMBER | COUNTRY | LANG |
+--------+---------+-------------+-------------+
| 1 | 3968 | UK | English |
| 2 | 3968 | Spain | Spanish |
| 3 | 3968 | USA | English |
| 4 | 1234 | Greece | Greek |
| 5 | 1234 | Italy | Italian |
I want to perform one query which only selects the unique 'NUMBER' column (whether is be the first or last row doesn't bother me). So this would give me:
+--------+---------+-------------+-------------+
| ID | NUMBER | COUNTRY | LANG |
+--------+---------+-------------+-------------+
| 1 | 3968 | UK | English |
| 4 | 1234 | Greece | Greek |
How is this achievable?
A very typical approach to this type of problem is to use row_number():
select t.*
from (select t.*,
row_number() over (partition by number order by id) as seqnum
from t
) t
where seqnum = 1;
This is more generalizable than using a comparison to the minimum id. For instance, you can get a random row by using order by newid(). You can select 2 rows by using where seqnum <= 2.
Since you don't care, I chose the max ID for each number.
select tbl.* from tbl
inner join (
select max(id) as maxID, number from tbl group by number) maxID
on maxID.maxID = tbl.id
Query Explanation
select
tbl.* -- give me all the data from the base table (tbl)
from
tbl
inner join ( -- only return rows in tbl which match this subquery
select
max(id) as maxID -- MAX (ie distinct) ID per GROUP BY below
from
tbl
group by
NUMBER -- how to group rows for the MAX aggregation
) maxID
on maxID.maxID = tbl.id -- join condition ie only return rows in tbl
-- whose ID is also a MAX ID for a given NUMBER
You will use the following query:
SELECT * FROM [table] GROUP BY NUMBER;
Where [table] is the name of the table.
This provides a unique listing for the NUMBER column however the other columns may be meaningless depending on the vendor implementation; which is to say they may not together correspond to a specific row or rows.