Related
I'm trying to optimize the filtering of data in one report/table and I've encountered a challenge.
Table is located in m.access, so any vba access code or sql query should work here.
So far I've tried few options, but could not achieve expected results:
select prev_type, type, next_type
from (
select *,
lag(type) over (order by id) as prev_type,
type,
lead(type) over (order by id) as next_type
from table
) as t
where type = "type";
Basically I want to display from below table three rows:
row with Type = 'D'
previous row to the one with Type 'D'
next row to the one with Type 'D'
enter image description here
Try with a subquery:
Select * From YourTable
Where Abs([ID] - (Select ID From YourTable Where [Type] = 'D')) <= 1
For multiple Ds, join the subquery:
Select
*
From
YourTable ,
(Select ID From YourTable Where [Type] = 'D') As T
Where
Abs(YourTable.[ID] - T.ID) <= 1
Merge three tables in a Select query by rule 3, 2, 1 records from each table as follows:
TableA: ID, FieldA, FieldB, FieldC,....
TableB: ID, FieldA, FieldB, FieldC,....
TableC: ID, FieldA, FieldB, FieldC,....
ID : auto number in each table
FieldA will be unique in all three tables.
I am looking for a Select query to merge three tables as follows:
TOP three records from TableA sorted by ID
TOP two records from TableB sorted by ID
TOP 1 record from TableC sorted by ID
Repeat this until select all records from all three tables.
If some table has fewer records or does not meet the criteria, ignore that and continue with others.
My attempt:
I did it totally through programming way, like cursors and If conditions inside a SQL Server stored procedure.
It makes delay.
This requires a formula that takes row numbers from each table and transforms it into a series of integers that skips the desired values.
In the query below, I am adding some CTE for the sake of shortening the formula. The real magic is in the UNION. Also, I am adding an additional field for your control. Feel free to get rid of it.
WITH A_Aux as (
SELECT 'A' As FromTable, ROW_NUMBER() OVER (ORDER BY ID) AS RowNum, TableA.*
FROM TableA
), B_Aux AS (
SELECT 'B' As FromTable, ROW_NUMBER() OVER (ORDER BY ID) AS RowNum, TableB.*
FROM TableB
), C_Aux AS (
SELECT 'C' As FromTable, ROW_NUMBER() OVER (Order BY ID) AS RowNum, TableC.*
FROM TableC
)
SELECT *
FROM (
SELECT RowNum+3*FLOOR((RowNum-1)/3) As ColumnForOrder, A_Aux.* FROM A_Aux
UNION ALL
SELECT 3+RowNum+4*FLOOR((RowNum-1)/2), B_Aux.* FROM B_Aux
UNION ALL
SELECT 6*RowNum, C_Aux.* FROM C_Aux
) T
ORDER BY ColumnForOrder
PS: note the pattern Offset + RowNum + (6-N) * Floor((RowNum-1)/N) to group N records together (it of course simplifies a lot for TableC).
PPS: I don't have a SQL server at hand to test it. Let me know if there is a syntax error.
You may try this..
GO
select * into #temp1 from (select * from table1) as t1
select * into #temp2 from (select * from table2) as t2
select * into #temp3 from (select * from table3) as t3
select * into #final from (select col1, col2, col3 from #temp1 where 1=0) as tb
declare #i int
set #i=1
while( (select COUNT(*) from #temp1)>#i)
Begin
;with ct1 as (
select ROW_NUMBER() over (order by id) as Slno, * from #temp1
),ct2 as (
select ROW_NUMBER() over (order by id) as Slno, * from #temp2
),ct3 as (
select ROW_NUMBER() over (order by id) as Slno, * from #temp3
),cfinal as (
select top 3 * from #temp1
union all
select top 2 * from #temp2
union all
select top 1 * from #temp3
)
insert into #final ( col1 , col2, col3 )
select col1, col2, col3 from cfinal
delete from #temp1 where id in (select top 3 ID from #temp1)
delete from #temp2 where id in (select top 2 ID from #temp2)
delete from #temp3 where id in (select top 1 ID from #temp3)
set #i = #i+1
End
Select * from #final
Drop table #temp1
Drop table #temp2
Drop table #temp3
GO
First create temp table for all 3 tables with each insert delete the inserted record and this will result you the desired result, if nothing is missing from my side.
Please see to this if this works.
There is not a lot of information to go with here, but I assume you can use UNION to combine multiple statements.
SELECT * TableA ORDER BY ID DESC OFFSET 3 ROWS
UNION
SELECT * TableB ORDER BY ID DESC OFFSET 2 ROWS
UNION
SELECT * TableC ORDER BY ID DESC OFFSET 1 ROWS
Execute and see if this works.
/AF
From my understanding, I create three temp tables as ta, tb, tc.
select * into #ta from (
select 'A' a
union all
select 'A' a
union all
select 'A' a
union all
select 'A' a
union all
select 'A' a
union all
select 'A' a
union all
select 'A' a
) a
select * into #tb from (
select 'B' b
union all
select 'B'
union all
select 'B'
union all
select 'B'
union all
select 'B'
) b
select * into #tc from (
select 'C' c
union all
select 'C'
union all
select 'C'
union all
select 'C'
union all
select 'C'
) c
If tables match you tables, then the output looks like A,A,A,B,B,C,A,A,A,B,B,C,A,B,C,C,C
T-SQL
declare #TAC int = (select count (*) from #ta) -- Table A Count = 7
declare #TBC int = (select count (*) from #tb) -- Table B Count = 5
declare #TAR int = #TAC % 3 -- Table A Reminder = 1
declare #TBR int = #TBC % 2 -- Table B Reminder = 1
declare #TAQ int = (#TAC - #TAR) / 3 -- Table A Quotient = (7 - 1) / 3 = 2, is will passed on NTILE
-- So we gonna split as two group (111), (222)
declare #TBQ int = (#TBC - #TBR) / 2 -- Table B Quotient = (5 - 1) / 2 = 2, is will passed on NTILE
-- So we gonna split as two group (11), (22)
select * from (
select *, NTILE (#TAQ) over ( order by a) FirstOrder, 1 SecondOrder from (
select top (#TAC - #TAR) * from #ta order by a
) ta -- 6 rows are obtained out of 7.
union all
select *, #TAQ + 1, 1 from (
select top (#TAR) * from #ta order by a desc
) ta -- Remaining one row is obtained. Order by desc is must
-- Here FirstOrder is next value of previous value.
union all
select *, NTILE (#TBQ) over ( order by b), 2 from (
select top (#TBC - #TBR) * from #tb order by b
) tb
union all
select *, #TBQ + 1, 2 from (
select top (#TBR) * from #tb order by b desc
) tb
union all
select *, ROW_NUMBER () over (order by c), 3 from #tc
) abc order by FirstOrder, SecondOrder
Let me explain the T-SQL:
Before that, FYR: NTILE and Row Number
Get the count.
Find the Quotient which will pass to NTILE function.
Order by the NTILE value and static.
Note:
I am using SQL Server 2017.
If T-SQL works fine, then you need to change the column in order by <yourcolumn>.
EDITED THE WHOLE TOPIC.
I need to create a view that sort article per type.
If I only have the type : *VALUE -> I need to show this line only.
If I have the type : *VALUE & 2 -> Still showing row accordingly to *VALUE type only.
If I only have the type : 2 -> Showing this one.
I already did somethink like this :
VALUE* is a value that should come from an another table with a Join.
SELECT Id_item ,Name_item , Type_item , Id_type_item FROM ITEM
WHERE Name_item = 'Gillette' AND (Id_Type_item = VALUE* OR Id_Type_item ='10')
ORDER BY CASE
WHEN row_number() OVER(ORDER BY Id_item DESC , Id_Type_Item DESC) <= 1 THEN 0
ELSE 1
END;
But it does that in the case where we've got both row for the types(*VALUE & 10):
Id_item / Name_item / Type_item / Id_Type_Item
1 Gillette 45 30 (*VALUE)
1 Gillette 2 10
So I think that the order by on the Over() could be useful to always sort by *VALUE (which are in reality another column from another table)
I always want to select 1 row of data only ! :)
I'm guessing, that what you want is the "first" row returned from each SELECT? There's no need to use a separate SELECT statement for each variable on the same table, you can use a window function to do so. I believe this is what you might be after.
WITH CTE AS(
--The following assumes table A and B have the same DDL (which begs the question, why are they different tables?)
SELECT *,
ROW_NUMBER() OVER (PARTITION BY var
ORDER BY (SELECT NULL)) AS RN --Replace SELECT(NULL) with your actual ordering criteria
FROM A
WHERE var IN (1,2)
UNION --ALL(?)
SELECT *
ROW_NUMBER() OVER (PARTITION BY var
ORDER BY (SELECT NULL)) AS RN --Replace SELECT(NULL) with your actual ordering criteria
FROM B
WHERE var IN (3))
SELECT *
FROM CTE
WHERE RN = 1;
Here is a possible solution. In this case ROW_NUMBER, RANK and DENSE_RANK would all work. However, ROW_COUNT is not a valid window function in sql server.
DECLARE #A TABLE(ID INT, Value INT)
DECLARE #B TABLE(ID INT,Value INT)
INSERT INTO #A VALUES (1,1),(2,1),(3,2),(4,3),(5,2),(6,1),(7,3)
INSERT INTO #B VALUES (1,1),(2,1),(3,1),(4,2),(5,3),(6,2),(7,1),(8,3)
;WITH D AS
(
SELECT ID,Value FROM #A WHERE Value IN(1,2)
UNION ALL
SELECT ID,Value FROM #B WHERE Value IN (3)
)
SELECT * FROM
(
SELECT
ID, Value,
ValueRankInSet = DENSE_RANK() OVER(PARTITION BY VALUE ORDER BY ID) -- <-- If you do not have an ID field you can subst ID with NEWID() as order is not important
FROM D
)AS X
WHERE ValueRankInSet = 1
Assign the priority within your Select(s) and then order by it in the row_number:
with cte as
(
SELECT *,
row_number()
over (-- partition by ???
order by prio) as Position
FROM
(
SELECT 1 as prio, * FROM A WHERE var = 1
UNION -- probably a more efficient UNION ALL
SELECT 2 as prio, * FROM A WHERE var = 2
UNION -- probably a more efficient UNION ALL
SELECT 3 as prio, * FROM B WHERE var = 3
)
)
select *
from cte
WHERE Position = 1
I'm trying to create a SQL Update statement that will read comma separated values in a text box field & update a table (1 column referencing ID) with these comma separated values.
Specifically I have a table of products that I want to create a form that updates the "Inventory" column in that table.
I update my inventory spreadsheet daily and it would be great if I can copy/paste the CSV of inventory for all my products paste into a textbox hit update and have my table actualized.
I'm not sure how to do this as I need to reference each products unique ID and update that ID with the matching value for inventory stock.
Any ideas, examples highly appreciated.
If you are using SQL Server 2016 then you can use string_split as I stated in the comment above
select * from string_split('1,2,3,4,5',',')
However:
If you are using earlier version of sql server < 2016 then you can create a function that returns a table then from there you can join to the table you need to update:
CREATE FUNCTION dbo.SplitStringToValues
(
#List NVARCHAR(MAX),
#Delimiter NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING AS
RETURN
WITH E1(N) AS ( SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1),
E2(N) AS (SELECT 1 FROM E1 a, E1 b),
E4(N) AS (SELECT 1 FROM E2 a, E2 b),
E42(N) AS (SELECT 1 FROM E4 a, E2 b),
cteTally(N) AS (SELECT 0 UNION ALL SELECT TOP (DATALENGTH(ISNULL(#List,1)))
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E42),
cteStart(N1) AS (SELECT t.N+1 FROM cteTally t
WHERE (SUBSTRING(#List,t.N,1) = #Delimiter OR t.N = 0))
SELECT Item = SUBSTRING(#List, s.N1, ISNULL(NULLIF(CHARINDEX(#Delimiter,#List,s.N1),0)-s.N1,8000))
FROM cteStart s;
go
Then use the function like so
SELECT * FROM SplitStringToValues('1,2,3,5',',') as t
UPDATE:
To get the next row's value next to the id as you have asked in the comment you could do something like this below. It is using the LEAD function introduced in SQL SERVER 2012:
SELECT Id, Value
FROM (SELECT
ROW_NUMBER() over (order by(select 1)) as cnt,
t.item AS Id,
Lead(t.item)
OVER (
ORDER BY (SELECT 1)) Value
FROM dbo.Splitstringtovalues('10,20,30,40,50,10,20,30,40,50,60,70', ',')
t)
keyValue
WHERE keyValue.value IS NOT NULL
and cnt % 2 = 1
If you are using SQL Server 2016 then you can use string_split
select * from string_split('1,2,3,4,5',',')
Let's suppose that I've got as a result from one query the next set of values of one column:
Value
1 A
2 B
3 C
4 D
5 E
6 F
7 G
8 H
9 I
10 J
Now, I would like to see this information with another order, establishing a limit to the number of values of every single subset. Now suppose that I choose 3 as a limit,the information will be given like this (one column for all the subsets):
Values
1 A, B, C
2 D, E, F
3 G, H, I
4 J,
Obviously, the last row will contain the remaining values when their number is smaller than the limit established.
Is it possible to perform a query like this in SQL?
What about if the limit is dynamic?. It can be chosen randomly.
create table dee_t (id int identity(1,1),name varchar(10))
insert into dee_t values ('A'),('B'),('c'),('D'),('E'),('F'),('g'),('H'),('I'),('J')
;with cte as
(
select (id-1)/3 +1 rno ,* from dee_t
) select rno ,
(select name+',' from cte where rno = c.rno for xml path('') )
from cte c group by rno
You can do this by using few calculations with row_number, like this:
select
GRP,
max(case when RN = 1 then Value end),
max(case when RN = 2 then Value end),
max(case when RN = 0 then Value end)
from (
select
row_number() over (order by Value) % 3 as RN,
(row_number() over (order by Value)+2) / 3 as GRP,
Value
from Table1
) X
group by GRP
The first row_number creates numbers for the columns (1,2,0,1,2,0...) and the second one creates numbers for the rows (1,1,1,2...). Those are then used to group the values into correct place using case, but you can also use pivot instead of it if you like it more.
If you want them into same column, of course just concatenate the cases instead of selecting them on different columns, but beware of nulls.
Example in SQL Fiddle
Thanks a lot for all your reply. Finally I've got a Solution with the help of Rajen Singh
This is the code than can be used:
WITH CTE_0 AS
(
SELECT DISTINCT column_A_VALUE AS id
FROM Table
WHERE column_A_VALUE IS NOT NULL
), CTE_1 AS
(
SELECT ROW_NUMBER() OVER (ORDER BY id) RN, id
FROM CTE_0
), CTE_2 AS
(
SELECT RN%30 GROUP, ID
FROM CTE_1
)SELECT STUFF(( SELECT ','''+CAST(ID AS NVARCHAR(20))+''''
FROM CTE_2
WHERE GROUP = A.GROUP
FOR XML PATH('')),1,1,'') IDS
FROM CTE_2 A
GROUP BY GROUP