sql question - Select columns and show in different rows - sql

thanks to this forum, I have found many answers to my questions.
I have a sql question . Hope someone can help.
There is a table with 2 columns for simplicity sake. Unsorted.
Numbers
Letters
1
A
2
B
1
C
3
S
2
L
etc..
How can I select multiple coulmns and put in different rows?
FOr ex- output should be as an added column
Newcol
1
A
C
2
B
L
3
S
I tried Lag, Lead , CTE etc...I am unable to produce the desired results. any help is appreciated.
thanks
INSERT INTO T1
(numbers, letters)
VALUES
('1', '1A'),
('1', '1B'),
('1', '1C'),
(3, '3B'),
(1, '3C'),
(3, '3D'),
(2, '2A'),
(4, '2B'),
(7, '2C'),
(3, '3A')
;
;
with cte as (select top(1000000) numbers, letters
from T1
order by numbers)
select numbers, letters as newcol
from cte order by cte.numbers, cte.letters;
i need ordered by first column - then second column. is it possible? Any help appreciated. thanks

Below code will resolve your problem:-
SELECT numbers, letters,
ROW_NUMBER() OVER (PARTITION BY numbers ORDER BY letters) as newcol
FROM T1
ORDER BY numbers, letters

Related

An association x-reference table

I've got a SQL table like below where one value is linked to a second value and vice versa.
ROW
ID1
ID2
1
1
2
2
2
1
3
3
4
4
4
3
....
This might be some bad design but this is what I'm stuck with. I need to produce a SQL query in SQL Server to return only the following (doesn't matter which order):
ROW
ID1
ID2
1
1
2
3
3
4
....
OR
ROW
ID1
ID2
2
2
1
4
4
3
....
I've got a list of ID's (1, 2, 3, 4) which I used to query the table against ID1 field or ID2 field, but it always returns all the rows because those IDs exist in both columns.
I've tried looking at eliminating one row by looking if the one field it exists in the other column, but then I get no results. Obviously.
The one solution that could work is by looking at the rownum field and only get the even or odd rows. But this feels hacky. Also there might be other values in that list that is not part of my IN list, so that could possibly miss some rows?
Anything eloquent to consider from a TSQL perspective
Here's one (quite cumbersome but pretty effective) way to do it.
First, Create and populate sample table (Please save us this step in future questions):
CREATE TABLE Table1 (
[ROW] int,
[ID1] int,
[ID2] int
);
INSERT INTO Table1 ([ROW], [ID1], [ID2]) VALUES
(1, 1, 2),
(2, 2, 1),
(3, 3, 4),
(4, 4, 3),
(5, 1, 4);
Note: The last raw is not a part of the sample data you've provided, but I assumed you would also like to include in the results records where only one row had the connection beteween Id1 and Id2.
Then, use a couple of common table expression to get the minimum row number of any pair of Id1 and Id2, regardless of the order of ids, and then query the original table joined to the second cte:
WITH CTE1 AS
(
SELECT Row,
IIF(Id1 < Id2, Id1, Id2) As Small,
IIF(Id1 < Id2, Id2, Id1) As Big
FROM Table1
), CTE2 AS
(
SELECT Min(Row) As MinRow
FROM CTE1
GROUP BY Small, Big
)
SELECT Row, Id1, Id2
FROM Table1
JOIN CTE2
ON Row = MinRow;
Results:
Row Id1 Id2
1 1 2
3 3 4
5 1 4
You can see a live demo on DB<>Fiddle

Select UNIQUE, NOT DISTINCT values

I am trying to select values from a table that are not duplicates - for example, with the following input set, I would like to select only the values in Column 1 that don't have a duplicated value in Column 2
Column 1 Column 2
A X
B X
C Y
D Y
E Z
Resulting in
Column 1 Column 2
E Z
This is made harder by my having a character limit for my SQL statement, and my having to join a couple of tables in the same query.
My existing statement is here, and this is where I am stuck.
SELECT d.o_docguid, d.o_itemdesc
FROM dms_doc d
INNER JOIN
(SELECT s.o_itemno as si, s.o_projectno as sp, t.o_itemno as ti, t.o_projectno as tp
FROM env_bs1192_1 s, env_bs1192_2 t
WHERE s.TB_FILE_ID = t.TB_FILE_ID) as r
ON (si = d.o_itemno AND sp = d.o_projectno)
OR (ti = d.o_itemno AND tp = d.o_projectno)
Results look like
o_docguid o_itemdesc
aguid adescription
bguid adescription
cguid bdescription
I want to filter this list out such that all that remains are the unique descriptions and their associated guid (i.e. only the rows that have specifically a single unique entry in the description, or put another way, if there is a duplicate, throw both away - in this instance, cguid and bdescription should be the only results).
The last challenge, which I still haven't solved, is that this SQL statement needs to fit into a character limit of 242 characters.
Taking the first part as a question, the answer might be:
declare #Table table (Column1 char(1), Column2 char(1));
insert into #Table values
('A', 'X'),
('B', 'X'),
('C', 'Y'),
('D', 'Y'),
('E', 'Z');
select
Column1 = max(Column1),
Column2
from
#Table
group by
Column2
having
count(*) = 1;
How to do it with generic data.
DROP TABLE IF EXISTS #MyTable
CREATE TABLE #MyTable(Column1 VARCHAR(50),Column2 VARCHAR(50))
INSERT INTO #MyTable(Column1,Column2)
VALUES
('A','X'),
('B','X'),
('C','Y'),
('D','Y'),
('E','Z')
;WITH UniqueCol2 AS
(
SELECT Column2
FROM #MyTable
GROUP BY Column2
HAVING COUNT(*) = 1
)
SELECT
mt.*
FROM UniqueCol2
JOIN #MyTable mt ON mt.Column2 = UniqueCol2.Column2

Comparing a value of a row with the value of the previous row

I have a table in SQL Server that stores geology samples, and there is a rule that must be adhered to.
The rule is simple, a "DUP_2" sample must always come after a "DUP_1" sample (sometimes they are loaded inverted)
CREATE TABLE samples (
id INT
,name VARCHAR(5)
);
INSERT INTO samples VALUES (1, 'ASSAY');
INSERT INTO samples VALUES (2, 'DUP_1');
INSERT INTO samples VALUES (3, 'DUP_2');
INSERT INTO samples VALUES (4, 'ASSAY');
INSERT INTO samples VALUES (5, 'DUP_2');
INSERT INTO samples VALUES (6, 'DUP_1');
INSERT INTO samples VALUES (7, 'ASSAY');
id
name
1
ASSAY
2
DUP_1
3
DUP_2
4
ASSAY
5
DUP_2
6
DUP_1
7
ASSAY
In this example I would like to show all rows where name equal to 'DUP_2' and predecessor row (using ID) name is different from 'DUP_1'.
In this case, it would be row 5 only.
I would appreciate very much if you help me.
You can use the LAG() window function or you can use LEAD() - they are identical except for the way in which they are ordered. That is - LAG(name) OVER ( ORDER BY id ) is the same as LEAD(name) OVER ( ORDER BY id DESC ). (You can read more about these functions here.)
WITH s1 ( id, name, prior_name ) AS (
SELECT id, name, LAG(name) OVER ( ORDER BY id ) AS prior_name
FROM samples
)
SELECT id, name
FROM s1
WHERE name = 'DUP_2'
AND COALESCE(prior_name, 'DUMMY') != 'DUP_1';
The reason for the COALESCE() at the end with the DUMMY value is that the first value won't have a LAG(); it will be NULL; and we want to return the DUP_2 record in this case since it doesn't follow a DUP_1 record.
You can use lag():
select s.*
from (select s.*,
lag(name) over (order by id) as prev_name
from samples s
) s
where name = 'DUP_2' and (prev_name <> 'DUP_1' or prev_name is null)

Select every possible combination below a certain sum?

I have a table called Item which has an ID, a Name and a Price.
Is it possible using SQL select statements to get every possible, but distinct combination of items below a specific price?
For example, assume this table:
ID Name Price
-- ---- -----
1 A 1
2 B 2
3 C 3
4 D 4
5 E 5
The query, taken the limit is for example 10, shall only return A, B, C, D, but not A, B, D, C additionally.
Is something like this even possible? Please excuse this probably stupid question, but I'm learning SQL for a year now, but our teacher hasn't even explained what SQL means. My entire knowledge is from books, so I'm not sure if this is a suitable question or not.
Well, since no inspiration was generated by Getting all possible combinations which obey certain condition with MS SQL, here's an adaptation of the code found there ;)
declare #data table (id int not null primary key, name varchar(40) not null, price money not null);
insert into #data (id, name, price) values (1, 'A', 1), (2, 'B', 2), (3, 'C', 3), (4, 'D', 4), (5, 'E', 5);
-- Replace #data with actual table name and delete the above
with
anchor as (
select id, name, price
from #data
),
cte as (
select
id as max_id,
price,
'|' + cast(id as varchar(max)) + '|' as level
from anchor
union all
select
a.id as max_id,
c.price + a.price,
c.level + '|' + cast(a.id as varchar(max)) + '|' as level
from
cte c
inner join anchor a on a.id > c.max_id and c.level not like '%|' + cast(a.id as varchar(max)) + '|%'
)
select level
from cte
where price <= 10
;
It's a good question, but as far as I know it's not possible with a set-based approach. It might be possible with some sort of CTE-based recursion, but if so, I can't think of how. It could be done with cursors, but not directly with "SQL select statements" (which I interpret to mean a set-based approach).
If you wanted to find all combinations of exactly two numbers that were less then x, you could cross join the table to itself (eliminating rows where the ID was the same) and sum the two prices, excluding sums that were less than x.
If you want combinations of 2 items, it is simple, just join the table with itself with no conditions (so you do the cartesian product) and then sum the price of the items in both tables (you can exclude the same item twice, if that is what you need).
IT is not possible to generalize for N items in SQL by definition since it is not possible in relational algebra. You would need a query with N lines with N being the number of records, so you would need to know a max number of records in advance.
Also, the combinatorial of N elements (if that is what you need) is O(N!) so it grows really fast in both size and possibilities, so if you have too many elements and a price large enough you would soon face the limits of any possible computer.

Continuous sequences in SQL

Having a table with the following fields:
Order,Group,Sequence
it is required that all orders in a given group form a continuous sequence. For example: 1,2,3,4 or 4,5,6,7. How can I check using a single SQL query what orders do not comply with this rule? Thank you.
Example data:
Order Group Sequence
1 1 3
2 1 4
3 1 5
4 1 6
5 2 3
6 2 4
7 2 6
Expected result:
Order
5
6
7
Also accepted if the query returns only the group which has the wrong sequence, 2 for the example data.
Assuming that the sequences are generated and therefore cannot be duplicated:
SELECT group
FROM theTable
GROUP BY group
HAVING MAX(Sequence) - MIN(Sequence) &lt> (COUNT(*) - 1);
How about this?
select Group from Table
group by Group
having count(Sequence) <= max(Sequence)-min(Sequence)
[Edit] This assumes that Sequence does not allow duplicates within a particular group. It might be better to use:count != max - min + 1
[Edit again] D'oh, still not perfect. Another query to flush out duplicates would take care of that though.
[Edit the last] The original query worked fine in sqlite, which is what I had available for a quick test. It is much more forgiving than SQL server. Thanks to Bell for the pointer.
Personaly I think I would consider rethinking the requirement. It is the nature of relational databases that gaps in sequences can easily occur due to records that are rolled back. For instance, supppose an order starts to create four items in it, but one fails for some rason and is rolled back. If you precomputed the sequences manually, you would then have a gap is the one rolled back is not the last one. In other scenarios, you might get a gap due to multiple users looking for sequence values at approximately the same time or if at the last minute a customer deleted one record from the order. What are you honestly looking to gain from having contiguous sequences that you don't get from a parent child relationship?
This SQL selects the orders 3 and 4 wich have none continuous sequences.
DECLARE #Orders TABLE ([Order] INTEGER, [Group] INTEGER, Sequence INTEGER)
INSERT INTO #Orders VALUES (1, 1, 0)
INSERT INTO #Orders VALUES (1, 2, 0)
INSERT INTO #Orders VALUES (1, 3, 0)
INSERT INTO #Orders VALUES (2, 4, 0)
INSERT INTO #Orders VALUES (2, 5, 0)
INSERT INTO #Orders VALUES (2, 6, 0)
INSERT INTO #Orders VALUES (3, 4, 0)
INSERT INTO #Orders VALUES (3, 6, 0)
INSERT INTO #Orders VALUES (4, 1, 0)
INSERT INTO #Orders VALUES (4, 2, 0)
INSERT INTO #Orders VALUES (4, 8, 0)
SELECT o1.[Order]
FROM #Orders o1
LEFT OUTER JOIN #Orders o2 ON o2.[Order] = o1.[Order] AND o2.[Group] = o1.[Group] + 1
WHERE o2.[Order] IS NULL
GROUP BY o1.[Order]
HAVING COUNT(*) > 1
So your table is in the form of
Order Group Sequence
1 1 4
1 1 5
1 1 7
..and you want to find out that 1,1,6 is missing?
With
select
min(Sequence) MinSequence,
max(Seqence) MaxSequence
from
Orders
group by
[Order],
[Group]
you can find out the bounds for a given Order and Group.
Now you can simulate the correct data by using a special numbers table, which just contains every single number you could ever use for a sequence. Here is a good example of such a numbers table. It's not important how you create it, you could also create an excel file with all the numbers from x to y and import that excel sheet.
In my example I assume such a numbers table called "Numbers" with only one column "n":
select
[Order],
[Group],
n Sequence
from
(select min(Sequence) MinSequence, max(Seqence) MaxSequence from [Table] group by [Order], [Group]) MinMaxSequence
left join Numbers on n >= MinSequence and n <= MaxSequence
Put that SQL into a new view. In my example I will call the view "vwCorrectOrders".
This gives you the data where the sequences are continuous. Now you can join that data with the original data to find out which sequences are missing:
select
correctOrders.*
from
vwCorrectOrders co
left join Orders o on
co.[Order] = o.[Order]
and co.[Group] = o.[Group]
and co.Sequence = o.Sequence
where
o.Sequence is null
Should give you
Order Group Sequence
1 1 6
After a while I came up with the following solution. It seems to work but it is highly inefficient. Please add any improvement suggestions.
SELECT OrdMain.Order
FROM ((Orders AS OrdMain
LEFT OUTER JOIN Orders AS OrdPrev ON (OrdPrev.Group = OrdMain.Group) AND (OrdPrev.Sequence = OrdMain.Sequence - 1))
LEFT OUTER JOIN Orders AS OrdNext ON (OrdNext.Group = OrdMain.Group) AND (OrdNext.Sequence = OrdMain.Sequence + 1))
WHERE ((OrdMain.Sequence < (SELECT MAX(Sequence) FROM Orders OrdMax WHERE (OrdMax.Group = OrdMain.Group))) AND (OrdNext.Order IS NULL)) OR
((OrdMain.Sequence > (SELECT MIN(Sequence) FROM Orders OrdMin WHERE (OrdMin.Group = OrdMain.Group))) AND (OrdPrev.Order IS NULL))