Distinct 2 field AS one column SQL SERVER - sql-server

can i SELECT distinct 2 fields (provfrom, provto) on table AS one column
with condition :
- values of 2 fields is never same in one row
- values in field provfrom can be inside field provto but in different row
- values in field provto can be inside field provfrom but in different row
example :
i have 2 column as below
-------------------------
| provfrom | provto |
-------------------------
| 2 | 4 |
| 3 | 7 |
| 3 | 7 |
| 5 | 2 |
| 5 | 2 |
| 7 | 2 |
| 7 | 2 |
| 1 | 5 |
| 2 | 5 |
| 2 | 8 |
| 5 | 8 |
-------------------------
the result that i want by disticnt is as below
-------------
| prov |
-------------
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 7 |
| 8 |
-------------
Can i do this in sql server?
i try to found out by explore google, but not found it
Thanks

You can use Union keyword which will give distinct elements from Both Tables
select provfrom from mytable
union
select provTo from mytable

You can either do this with a union or by using apply, the apply has less IO so I would go with the apply query.
create table #temp
(
provfrom tinyint,
provto tinyint
);
insert into #temp (provfrom, provto)
values (2,4),(3,7),(3,7),
(5,2),(5,2),(7,2),
(7,2),(1,5),(2,5),
(2,8),(5,8);
set statistics io on;
select distinct
a.provfromto
from #temp as t
cross apply (values (t.provfrom),(t.provto)) as a(provfromto);
select provfrom from #temp
union
select provTo from #temp
set statistics io off;
drop table #temp;

Try this:
select t.prov
from
(select provfrom as prov
from yourtable
union
select provto
from yourtable) as t
order by t.prov
UNION function apply a distinct clause, so you'll get all value per one occurence.
The external query about ordering your result set

Related

SQL Group By a Partition By

This must be accomplished in MS SQL Server. I believe OVER( PARTITION BY) must be used, but I've failed at all my tries and I end up counting the records to each ID or something else...
I have this table:
| ID | COLOR |
+------+--------+
| 1 | Red |
| 1 | Green |
| 1 | Blue |
| 2 | Red |
| 2 | Green |
| 2 | Blue |
| 3 | Red |
| 3 | Brown |
| 3 | Orange |
Notice that ID = 1 and ID = 2 have precisely the same values for COLOR, however ID = 3 only shares the value COLOR = Red.
I would like to group the table as follows:
| COLOR | COUNT | GROUPING |
+--------+-------+----------+
| Red | 2 | Type 1 |
| Green | 2 | Type 1 |
| Blue | 2 | Type 1 |
| Red | 1 | Type 2 |
| Brown | 1 | Type 2 |
| Orange | 1 | Type 2 |
This would mean that ID = 1 and ID = 2 share the same 3 values for color and they are aggregated together as Type 1. Although ID = 3 shares one value for color to ID = 1 and ID = 2 (which is 'Red') the rest of the values are not shared, as such it is considered of Type 2 (different grouping).
The tables used are simple examples and are enough to replicate to the entire dateset, however each ID can have in theory hundreds of records with different values for colors in each row. However they are unique, one ID can't have the the same color in different rows.
My best attempt:
SELECT
ID,
COLOR,
CONCAT ('TYPE ', COUNT(8) OVER( PARTITION by ID)) AS COLOR_GROUP
FROM
{TABLE};
Result:
| ID | COLOR | GROUPING |
+------+--------+----------+
| 1 | Green | Type 3 |
| 1 | Blue | Type 3 |
| 1 | Red | Type 3 |
| 2 | Green | Type 3 |
| 2 | Blue | Type 3 |
| 2 | Red | Type 3 |
| 3 | Red | Type 3 |
| 3 | Brown | Type 3 |
| 3 | Orange | Type 3 |
Although the results are terrible I've tried different methods, none of them is better.
Hope I was clear enough.
Thank you for the help!
try the following:
declare #t table ( ID int,COLOR varchar(100))
insert into #t select 1 ,'Red'
insert into #t select 1 ,'Green'
insert into #t select 1 ,'Blue'
insert into #t select 2 ,'Red'
insert into #t select 2 ,'Green'
insert into #t select 2 ,'Blue'
insert into #t select 3 ,'Red'
insert into #t select 3 ,'Brown'
insert into #t select 3 ,'Orange'
select *, STUFF((SELECT CHAR(10) + ' '+COLOR
FROM #t t_in where t_in.ID=t.ID
order by COLOR
FOR XML PATH ('')) , 1, 1, '') COLOR_Combined
into #temp
from #t t
select COLOR, count(color) [COUNT], 'TYPE ' + convert(varchar(10), dense_rank() OVER (order by [grouping])) [GROUPING]
from
(
select id, COLOR, COLOR_Combined, (row_number() over (order by id) - row_number() over (partition by Color_Combined order by id)) [grouping]
from #temp
)t
group by COLOR, [grouping]
drop table if exists #temp
Please find the db<>fiddle here.

Find records of nearest date SQL

I have a table dbo.X with DateTime column lastUpdated and a code product column CodeProd which may have hundreds of records, with CodeProd duplicated because the table is used as "stock history"
My Stored Procedure has parameter #Date, I want to get all CodeProd nearest to that date so for example if I have:
+----------+--------------+--------+
| CODEPROD | lastUpdated | STATUS |
+----------+--------------+--------+
| 10 | 2-1-2019 | C1 |
| 10 | 1-1-2019 | C2 |
| 10 | 31-12-2019 | C1 |
| 11 | 31-12-2018 | C1 |
| 11 | 30-12-2018 | C1 |
| 12 | 30-8-2018 | C3 |
+----------+--------------+--------+
and #Date= '1-1-2019'
I wanna get:
+----+--------------+------+
| 10 | 1-1-2019 | C2 |
| 11 | 31-12-2018 | C1 |
| 12 | 30-8-2018 | C3 |
+----+--------------+------+
How to find it?
You can use TOP(1) WITH TIES to get one row with nearest date for each CODEPROD which should be less than provided date.
Try like following code.
SELECT TOP(1) WITH TIES *
FROM [YourTableName]
WHERE lastupdated <= #date
ORDER BY Row_number()
OVER (
partition BY [CODEPROD]
ORDER BY lastupdated DESC);
You can use apply :
select distinct t.CODEPROD, t1.lastUpdated, t1.STATUS
from table t cross apply
( select top (1) t1.*
from table t1
where t1.CODEPROD = t.CODEPROD and t1.lastUpdated <= #date
order by t1.lastUpdated desc
) t1;

Update All other Records Based on a single record

I have a table with a million records. I need to update some columns which are null based on the existing 'not null' records of a particular id based columns. I've tried with one query, it seems to be working fine but I don't have confidence in it that it will be able to update all those 1 million records exactly the way I need. I'm providing you some sample data how my table looks like.Any help will be appreciated
SELECT * INTO #TEST FROM (
SELECT 1 AS EMP_ID,10 AS DEPT_ID,15 AS ITEM_NBR ,NULL AS AMOUNT,NULL AS ITEM_NME
UNION ALL
SELECT 1,20,16,500,'ABCD'
UNION ALL
SELECT 1,30,17,NULL,NULL
UNION ALL
SELECT 2,10,15,1000,'XYZ'
UNION ALL
SELECT 2,30,16,NULL,NULL
UNION ALL
SELECT 2,40,17,NULL,NULL
) AS A
Sample data:
+--------+---------+----------+--------+----------+
| EMP_ID | DEPT_ID | ITEM_NBR | AMOUNT | ITEM_NME |
+--------+---------+----------+--------+----------+
| 1 | 10 | 15 | NULL | NULL |
| 1 | 20 | 16 | 500 | ABCD |
| 1 | 30 | 17 | NULL | NULL |
| 2 | 10 | 15 | 1000 | XYZ |
| 2 | 30 | 16 | NULL | NULL |
| 2 | 40 | 17 | NULL | NULL |
+--------+---------+----------+--------+----------+
Expected result:
+--------+---------+----------+--------+----------+
| EMP_ID | DEPT_ID | ITEM_NBR | AMOUNT | ITEM_NME |
+--------+---------+----------+--------+----------+
| 1 | 10 | 15 | 500 | ABCD |
| 1 | 20 | 16 | 500 | ABCD |
| 1 | 30 | 17 | 500 | ABCD |
| 2 | 10 | 15 | 1000 | XYZ |
| 2 | 30 | 16 | 1000 | XYZ |
| 2 | 40 | 17 | 1000 | XYZ |
+--------+---------+----------+--------+----------+
I tried this but I'm unable to conclude whether it is updating all the 1 million records properly.
SELECT * FROM #TEST T
inner JOIN #TEST T1 ON T1.EMP_ID=T.EMP_ID
WHERE T1.AMOUNT IS NOT NULL
UPDATE T SET AMOUNT=T1.AMOUNT
FROM #TEST T
inner JOIN #TEST T1 ON T1.EMP_ID=T.EMP_ID
WHERE T1.AMOUNT IS not NULL
I have used UPDATE using inner join
UPDATE T
SET T.AMOUNT = X.AMT,T.ITEM_NME=X.I_N
FROM #TEST T
JOIN
(SELECT EMP_ID,MAX(AMOUNT) AS AMT,MAX(ITEM_NME) AS I_N
FROM #TEST
GROUP BY EMP_ID) X ON X.EMP_ID = T.EMP_ID
SELECT * into #Test1
FROM #TEST
WHERE AMOUNT IS NOT NULL
For records validation run this query first
SELECT T.AMOUNT, T1.AMOUNT, T1.EMP_ID,T1.EMP_ID
FROM #TEST T
inner JOIN #TEST1 T1 ON T1.EMP_ID=T.EMP_ID
WHERE T.AMOUNT IS NULL
Begin Trans
UPDATE T
SET T.AMOUNT=T1.AMOUNT, T.ITEM_NME= = T1.ITEM_NME
FROM #TEST T
inner JOIN #TEST1 T1 ON T1.EMP_ID=T.EMP_ID
WHERE T.AMOUNT IS NULL
rollback
SELECT EMP_ID,MAX(AMOUNT) as AMOUNT MAX(ITEM_NAME) as ITEM_NAME
INTO #t
FROM #TEST
GROUP BY EMP_ID
UPDATE t SET t.AMOUNT = t1.AMOUNT, t.ITEM_NAME = t1.ITEM_NAME
FROM #TEST t INNER JOIN #t t1
ON t.emp_id = t1.emp_id
WHERE t.AMOUNT IS NULL and t.ITEM_NAME IS NULL
Use MAX aggregate function to get amount and item name for each employee and then replace null values of amount and item name with those values. For validation use COUNT function to calculate the number of rows with values of amount and item name as null. If the number of rows is zero then table is updated correctly

update order column in a table but maintain the order

i have a table
id | title | F_ID | order
----------------------------
1 | test 1| 1 | 44
2 | test 3| 1 | 3
3 | test 4| 1 | 1
4 | test 5| 2 | 1
i want to update order column to +10 for all rows that have F_ID 1 but keep the order
the result need to be
id | title | F_ID | order
----------------------------
1 | test 1| 1 | 30
2 | test 3| 1 | 20
3 | test 4| 1 | 10
4 | test 5| 2 | 1
i can insert all rows that i want to update to temp table
and then loop the rows and update every row in the real table by [id].
maybe there is a better option?
I think this should work:
SQL Fiddle
MS SQL Server 2008 Schema Setup:
create table test (id int, title varchar(49), F_ID int, [order] int)
insert test values
(1 , 'test 1', 1, 44),
(2 , 'test 3', 1, 3),
(3 , 'test 4', 1, 1),
(4 , 'test 5', 2, 1)
Query 1:
update test
set [order] = new_order
from test t
inner join (
select
id,
new_order = ROW_NUMBER() over (partition by f_id order by [order]) * 10
from test t
where f_id = 1
) t2
on t.id = t2.id
Results:
Query 2:
select * from test
Results:
| ID | TITLE | F_ID | ORDER |
|----|--------|------|-------|
| 1 | test 1 | 1 | 30 |
| 2 | test 3 | 1 | 20 |
| 3 | test 4 | 1 | 10 |
| 4 | test 5 | 2 | 1 |
Well there might be a better solution then this but you can try this by using recursive CTE.
;WITH updCTE
AS
(
SELECT 30 AS YourOrder, 1 AS id
UNION ALL
SELECT YourOrder - 10 AS YourOrder, id + 1 AS id
FROM updCTE
WHERE YourOrder > 1
)
UPDATE YourTable
SET [order] = YourOrder
FROM updCTE
JOIN YourTable ON updCTE.id = YourTable.id
WHERE YourTable.F_ID = 1
ORDER BY YourTable.id

SQL Server 2005 T-SQL Problem: Need help in omitting records

Good day!
I need help in writing a query.. I have records in a table below.. The condition would be no records should be displayed if the succeeding records' new_state was repeated from the previous records(new_state) and if it is changed in the same date..
here record_id 1 has gone through the ff states: 0->1->2->1->3->4->3 in the same day.. state 1 was changed to state 2 then back to state 1 again (id 2 & 3 would not be displayed).. same with state 3 (id 5 & 6 would not be displayed)..
id | record_id| date_changed | old_state | new_state |
1 | 1 | 2009-01-01 | 0 | 1 |
2 | 1 | 2009-01-01 | 1 | 2 | not displayed
3 | 1 | 2009-01-01 | 2 | 1 | not displayed
4 | 1 | 2009-01-01 | 1 | 3 |
5 | 1 | 2009-01-01 | 3 | 4 | not displayed
6 | 1 | 2009-01-01 | 4 | 3 | not displayed
so the result would display only 2 records for record_id=1..
id | record_id| date_changed | old_state | new_state |
1 | 1 | 2009-01-01 | 0 | 1 |
4 | 1 | 2009-01-01 | 1 | 3 |
Here's the code for table creation and data:
IF OBJECT_ID('TempDB..#table','U') IS NOT NULL
DROP TABLE #table
CREATE TABLE #table
(
id INT identity primary key,
record_id INT,
date_changed DATETIME,
old_state INT,
new_state INT
)
INSERT INTO #table(record_id,date_changed,old_state,new_state)
SELECT 1,'2009-01-01',0,1 UNION ALL --displayed
SELECT 1,'2009-01-01',1,2 UNION ALL --not displayed
SELECT 1,'2009-01-01',2,1 UNION ALL --not displayed
SELECT 1,'2009-01-01',1,3 UNION ALL --displayed
SELECT 1,'2009-01-01',3,4 UNION ALL --not displayed
SELECT 1,'2009-01-01',4,3 --not displayed
INSERT INTO #table(record_id,date_changed,old_state,new_state)
SELECT 3,'2009-01-01',0,1 UNION ALL --displayed
SELECT 3,'2009-01-01',1,2 UNION ALL --not displayed
SELECT 3,'2009-01-01',2,3 UNION ALL --not displayed
SELECT 3,'2009-01-01',3,4 UNION ALL --not displayed
SELECT 3,'2009-01-01',4,1 --not displayed
SELECT * FROM #table
I would appreciate any help..
Thanks
For clarity regarding record_id=3.. Given this table:
id | record_id| date_changed | old_state | new_state |
7 | 3 | 2009-01-01 | 0 | 1 |
8 | 3 | 2009-01-01 | 1 | 2 | not displayed
9 | 3 | 2009-01-01 | 2 | 3 | not displayed
10 | 3 | 2009-01-01 | 3 | 4 | not displayed
11 | 3 | 2009-01-01 | 4 | 1 | not displayed
when running the query for record_id=3, the table result will be:
id | record_id| date_changed | old_state | new_state |
7 | 3 | 2009-01-01 | 0 | 1 |
Thanks!
UPDATE (12/2/2009):
Special scenario
id | record_id| date_changed | old_state | new_state |
1 | 4 | 2009-01-01 | 0 | 1 | displayed
2 | 4 | 2009-01-01 | 1 | 2 | displayed
3 | 4 | 2009-01-01 | 2 | 3 | not displayed
4 | 4 | 2009-01-01 | 3 | 2 | not displayed
5 | 4 | 2009-01-01 | 2 | 3 | displayed
6 | 4 | 2009-01-01 | 3 | 4 | not displayed
7 | 4 | 2009-01-01 | 4 | 3 | not displayed
where new_state 3 appears on id 3,5 and 7.. id 3 would not be displayed since it is between id 2 and id 4 which have the same new_state(3).. Then id 5 should be displayed since there is no existing new_state 3 yet..
code snippet:
IF OBJECT_ID('TempDB..#tablex','U') IS NOT NULL
DROP TABLE #tablex
CREATE TABLE #tablex
(
id INT identity primary key,
record_id INT,
date_changed DATETIME,
old_state INT,
new_state INT
)
INSERT INTO #tablex(record_id,date_changed,old_state,new_state)
SELECT 4,'2009-01-01',0,1 UNION ALL --displayed
SELECT 4,'2009-01-01',1,2 UNION ALL --displayed
SELECT 4,'2009-01-01',2,3 UNION ALL --not displayed
SELECT 4,'2009-01-01',3,2 UNION ALL --not displayed
SELECT 4,'2009-01-01',2,3 UNION ALL --displayed
SELECT 4,'2009-01-01',3,4 UNION ALL --not displayed
SELECT 4,'2009-01-01',4,3 --not displayed
I think the sequence in building the result is important..
Thanks!
SELECT A.*
/*
A.ID, A.old_state, a.new_state,
B.ID as [Next], b.old_state, b.new_state,
C.ID as [Prev], c.old_state, c.new_state
*/
FROM #table A LEFT JOIN
#table B ON A.ID = (B.ID - 1)
LEFT JOIN #table C ON (A.ID - 1) = C.ID
-- WHERE A.old_State <> B.new_State AND A.new_State <> C.old_State
WHERE A.record_id = 1
AND A.old_State <> COALESCE(B.new_State, -1)
AND A.new_State <> COALESCE(C.old_State, -1)
EDIT: I guess, what OP needs is that the remaining record should be selected except those where current record's old state is not the same as next record's new state (kind of an undo operation in records) and current record's new state should not be same as previous record's old state.
Following steps to get to the result
select all items that should not appear in the result.
left join these with the original table and select only those records that don't match a should not appear record.
.
;WITH cte_table (master_id, master_state, id, record_id, old_state, new_state, level) AS
(
SELECT id, old_state, id, record_id, old_state, new_state, 1
FROM #table
UNION ALL
SELECT master_id, master_state, #table.id, #table.record_id, #table.old_state, #table.new_state, level + 1
FROM cte_table
INNER JOIN #table ON cte_table.new_state = #table.old_state
AND cte_table.record_id = #table.record_id
AND cte_table.id < #table.id
AND cte_table.master_state < #table.old_state
)
SELECT master_id, t1.*, level
INTO #result
FROM #table t1
INNER JOIN (
SELECT master_id, min_child_id = MIN(id), level
FROM cte_table
GROUP BY master_id, level
) t2 ON t2.min_child_id = t1.id
SELECT t1.*
FROM #table t1
LEFT OUTER JOIN (
SELECT r1.id
FROM #result r1
INNER JOIN (
SELECT r1.master_id
FROM #result r1
INNER JOIN #result r2 ON r2.new_state = r1.old_state
AND r2.master_id = r1.master_id
WHERE r1.level = 1
) r2 ON r2.master_id = r1.master_id
) r1 ON r1.id = t1.id
WHERE r1.id IS NULL
AND t1.old_state < t1.new_state
ORDER BY 1, 2, 3

Resources