SQL Divide or group fixed lengthed entries together - sql

i have the following SQL Problem, given a table with a specific column e.g tableX:
col1
123
321
456
321
982
666
100
...
the amount of rows in this table can vary (even be 0).
What i need is to divide the rows into a coma separate text.
For example, I could put all values into one string
using this
SET #LIST= (SELECT ',''' + col1+'''' FROM Table FOR XML PATH(''))
('123','321','456','321',...) -- < can be too long
but the Problem is, that #LIST gets too long, therefore I want to divide it based on the number of entries into multiple (sub)lists. For example a fixed size (e.g Maximum 3 Elements) would always look at three Elements until nothing is left.
I was thinking of using some kindof Loop
// some Kind of Loop
('123,321,456')
// end some Kind of loop
and in the next Loop
('321,982,66')
and finally (if less than 3 are remaining) only
('100')
how can i achieve this?
edit: the database is a MSSQL db. if necessary I could sort the entries but they also contain characters (not only numerical). in fact the order doesnt matter.

Related

SQL: Change query output to have two separate columns from having rows with 2 values

For some reason my query output is grouping together 2 columns into 1, and putting the 2 values in the same row like this:
PATIENT_NAME
--------------------------------------------------------
INSURANCE
-------------------------
Aimie Pepsodent
Manulife
Aka Fresh
Blue Cross
Apple Addaye
Blue Cross
But I want them to appear in two separate columns like my teacher's output:
PATIENT_NAME INSURANCE
-------------- ----------------
Apple Addaye Blue Cross
Roy Alflush No Insurance
Shane Cane No Insurance
Is there a way I can change it to this?
Right now my sql query looks like this:
select (fname||' '||lname) patient_name,
(nvl(l4_insurance_cos.company_name, 'No Insurance')) insurance
from l4_patients
left join l4_insurance_cos
on l4_patients.ins_id = l4_insurance_cos.id
order by l4_patients.lname;
This is a pure SQLPlus display issue. The size of the line is too small for the two columns to fit in it, so SQLPlus splits the results on two lines.
You need to adjust the linesize of your terminal, and/or the display width of each column - by default, it corresponds to the maximum length of the resultset column (if you concatenate two columns in the query, that's the sum of the length of the two columns, with a limit of 4000 bytes for varchars).
The actual values will depend on your terminal and table definition, but here is an example:
set linesize 140 -- allow a total of 140 characters per line
column patient_name format a80 -- 80 characters for column "patient_name"
column insurance format a60 -- 60 characters for column "insurance"
Then, you can run your query.

How to insert uneven data rows into matrix in SAS?

I have an originations data set with loan ids. I then have a corresponding dataset with performance data for each of these loans ids, which can be anywhere from 10-40 rows in the performance data set.
The start date of each of the performance loans is not the same either, although some do overlap. What I want to do is take every loan id group in the performance data set, and then create a row of a certain column value across all occurrences in the data set. It doesn't matter if they start on different dates, I just want to align the values as this is the first value for loan id x and y.
For example:
ID Date Val
3 201601 100
3 201602 102
3 201603 103
--> Result:
ID Val1 Val2 Val3
3 100 102 103
I'm having two issues. One is the differing size of performance data for each id. I can't construct a matrix with differing lengths of rows. I'm assuming I'll need to append 0's to the end of each row to meet a predefined width.
My second issue is that I'm not sure how to read through a the performance data set to group loans, extract the value column, construct the column into a row for that id, and then insert into a matrix. I know how I would do this in Python but I need to use SAS. I can construct tables in SAS, but I'm not sure how to append rows, only columns.
If someone could provide some guidance on this it'd be a great help.
Anyone who runs into a similar issue it ended up being only a few lines of code.
proc transpose data = new_data
out = new_data1;
var trans_state;
by id;
run;
The output will be

How can I "dynamically" split a varchar column by specific characters?

I have a column that stores 2 values. Example below:
| Column 1 |
|some title1 =ExtractThis ; Source Title12 = ExtractThis2|
I want to remove 'ExtractThis' into one column and 'ExtractThis2' into another column. I've tried using a substring but it doesn't work as the data in column 1 is variable and therefore it doesn't always carve out my intended values. SQL below:
SELECT substring(d.Column1,13,24) FROM dbo.Table d
This returns 'Extract This' but for other columns it either takes too much or too little. Is there a function or combination of functions that will allow me to split consistently on the character? This is consistent in my column unlike my length count.
select substring(col1,CHARINDEX('=',col1)+1,CHARINDEX (';',col1)-CHARINDEX ('=',col1)-1) Val1,
substring(col1,CHARINDEX('=',col1,CHARINDEX (';',col1))+1,LEN(col1)) Val2
from #data
there is duplicate calculation that can be reduced from 5 to 3 to each line.
but I want to believe this simple optimization done by SQL SERVER.

What is the best way to reassign ordinal number of a move operation

I have a column in the sql server called "Ordinal" that is used to indicate the display order of the rows. It starts from 0 and skips 10 for the next row. so we have something like this:
Id Ordinal
1 0
2 20
3 10
It skips 10 because we wanted to be able to move item in between items (based on ordinal) without having to reassign ordinal number for the entire table.
As you can imagine eventually, Ordinal number will need to be reassign somehow for a move in between operation either on surrounding rows or for the entire table as the unused ordinal numbers between the target items are all used up.
Is there any algorithm that I can use to effectively reorder the ordinal number for the move operation taken in the consideration like long term maintainability of the table and minimizing update operations of the table?
You can re-number the sequences using a somewhat complicated UPDATE statement:
UPDATE u
SET u.sequence = 10 * (c.num_below-1)
FROM test u
JOIN (
SELECT t.id, count(*) AS num_below
FROM test t
JOIN test tr ON tr.sequence <= t.sequence
GROUP BY t.id
) c ON c.id=u.id
The idea is to obtain a count of items with the sequence lower than that of the current row, multiply the count by ten, and assign it as the new count.
The content of test before the UPDATE:
ID Sequence
__ ________
1 0
2 10
3 20
4 12
The content of test after the UPDATE:
ID Sequence
__ ________
1 0
2 30
3 10
4 20
Now the sequence numbers are evenly spread again, so you can continue inserting in the middle until you run out of new sequence numbers; then you can re-number again.
Demo.
These won't answer your question directly--I just thought I might suggest some other approaches:
One possibility--don't try to do it by hand. Have your software manage the numbers. If they need re-writing, just save them with new numbers.
a second--use a "Linked List" instead. In each record store the index of the next record you want displayed, then have your code load that directly into a linked list.
Yet another simple approach. Let's say you're inserting a new record with an ordinal equal x.
First, check if there's a row having ordinal value equal x. In case there's one, just update all the records having the ordinal value equal or bigger than x increasing them by y. Then, you are safe to insert a new record.
This way you're sure you'll not run update every time and of course, you'll keep the order.

How to get Next 4 digit number from Table (Contains 3,4, 5 & 6, digit long numbers)

I found a good method of getting the next 4 digit number.
How to find next free unique 4-digit number
But in my case I want to get next available 4 or 5 digit number.
And this will change depending upon the users request. Theses number are not a key ID columns, but they are essential to the tagging structure for the business purpose.
Currently I use a table adabpter query. But how would I write a query.
I suppose I could do a long iterative loop through all values until I see a 4 digit.
But I'm trying to think of something more efficient.
Function GetNextAvailableNumber(NumofDigits) as Long
'SQL Code Here ----
'Query Number Table
Return Long
End Function
Here's my current SQL:
'This Queries my View
SELECT MIN([Number]) AS Expr1
FROM LineNumbersNotUsed
'This is my View SQL
SELECT Numbers.Number
FROM Numbers
WHERE (((Exists (Select * From LineList Where LineList.LineNum = Numbers.Number))=False))
ORDER BY Numbers.Number;
Numbers is the List of All available number from 0 to 99999, basically what's available to use.
LineList is my final master table where I keep the long and all the relevant other business information.
Hopefully this make sense.
Gosh you guys are so tough on new guys.
I accidentally hit the enter key, and the question posted and I instantly get -3 votes.
Give a new guy a break will you! Please.
I apologize in advance in case I overlooked something in your question. Using your design, won't a query like this return the next unused 4 digit number?
SELECT MIN([Number]) AS next_number
FROM LineNumbersNotUsed
WHERE
[Number] > 999
AND [Number] < 10000;
This approach is not adequate with multiple concurrent users, but you didn't indicate that is an issue for you.
The question you linked to explains that what you need is a table with 2 fields:
Number InUse
0000 No
0001 No
0002 Yes
0003 No
0005 Yes
Whenever a number is used/released, the table must be updated to set InUse to Yes/No.
Maybe I'm missing something, but from your explanation, and the SQL code you show us, it seems that you only have a table with a single field containing all numbers from 0 to 100000.
If that's the case, I don't see the usefulness of that table at all.
If I were you, and if I understand your need correctly, what you want is something like this:
First of all, create the table as above, with all running numbers from 0 to 100000, and a field for confirming if that number is used or not.
Initialise the InUse field with all the numbers already taken in your LineList table, something like:
UPDATE Numbers SET InUse = True
WHERE Numbers.Number IN (SELECT LineNum FROM LineList)
Write a function ReserveNumber(NumOfDigits as Integer) As Long to find and reserve a 4-digit or 5-digit free number following this logical sequence:
Depending on NumOfDigits (4 or 5) get the result of one of the queries as LowestNumber:
SELECT Min(Number) FROM Numbers WHERE Number < 10000 AND NOT InUse
SELECT Min(Number) FROM Numbers WHERE Number >= 10000 AND NOT InUse
Reserve that particular number to ensure it's not going to be used again:
UPDATE Numbers SET InUse = True WHERE Number = #LowestNumber
Return LowestNumber
Whenever
Notes: the logic above is a bit naive as it suppose that no two users will attempt to get the lowest number at the same time. There is however a risk that this may happen one day.
To remove that risk, you can, for instance, add a TakenBy column to the Numbers table and set it to the current username. Then, after you have reserved the number, read-it again to ensure that the TakenBy is really updated by the current client. If not, just try gain.
There are lots of ways to do this. You can try to fiddle around table locks as well, but whatever your solution, make sure you test it.