Split row into multiple rows in SQL Server for insert - sql

I am trying to create a SQL query that will insert a single row as 2 rows in another table.
My data looks like this:
size | indemnity_factor | monitoring_factor
--------------------------------------------
0 | 1.00 | 1.5
The end data looks like this:
id | claim_component_type_code | size | adjustment_factor | valid_from_date
------------------------------------------------------------------------------
1 | Indemnity | 0 | 2.5000000 | 2014-01-01
1 | Monitoring | 1 | 1.5000000 | 2014-01-01
I want to add an entry of Indemnity and Monitoring for every row in the first data source. I haven't really got an idea how to go about it, would be very appreciative if someone could help. Sorry for the rough data but I can't post images with my reputation apparently.
Thanks in advance.

Use unpivot
select * from
(select size, indemnity_factor as indemnity, monitoring_factor as monitoring
from yourtable) src
unpivot (adjustment_factor for claim_component_type_code in (indemnity, monitoring) ) u

Related

SQL structure for multiple queries of the same table (using window function, case, join)

I have a complex production SQL question. It's actually PrestoDB Hadoop, but conforms to common SQL.
I've got to get a bunch of metrics from a table, a little like this (sorry if the tables are mangled):
+--------+--------------+------------------+
| device | install_date | customer_account |
+--------+--------------+------------------+
| dev 1 | 1-Jun | 123 |
| dev 1 | 4-Jun | 456 |
| dev 1 | 10-Jun | 789 |
| dev 2 | 20-Jun | 50 |
| dev 2 | 25-Jun | 60 |
+--------+--------------+------------------+
I need something like this:
+--------+------------------+-------------------------+
| device | max_install_date | previous_account_number |
+--------+------------------+-------------------------+
| dev 1 | 10-Jun | 456 |
| dev 2 | 25-Jun | 50 |
+--------+------------------+-------------------------+
I can do two separate queries to get max install date and previous account number, like this:
select device, max(install_date) as max_install_date
from (select [a whole bunch of stuff], dense_rank() over(partition by device order by [something_else]) rnk
from some_table a
)
But how do you combine them into one query to get one line for each device? I have rank, with statements, case statements, and one join. They all work individually but I'm banging my head to understand how to combine them all.
I need to understand how to structure big queries.
ps. any good books you recommend on advanced SQL for data analysis? I see a bunch on Amazon but nothing that tells me how to construct big queries like this. I'm not a DBA. I'm a data guy.
Thanks.
You can use correlated subquery approach :
select t.*
from table t
where install_date = (select max(install_date) from table t1 where t1.device = t.device);
This assumes install_date has resonbale date format.
I think you want:
select t.*
from (select t.*, max(install_date) over (partition by device) as max_install_date,
lag(customer_account) over (partition by device order by install-date) as prev_customer_account
from t
) t
where install_date = max_install_date;

Access query fails when WHERE statement is added to subquery referencing ODBC link

Original post
Given two tables structured like this:
t1 (finished goods) t2 (component parts)
sku | desc | fcst sku | part | quant
0001 | Car | 10000 0001 | wheel | 4
0002 | Boat | 5000 0001 | door | 2
0003 | Bike | 7500 0002 | hull | 1
0004 | Shirt | 2500 0002 | rudder | 1
... | ... | ... 0003 | wheel | 2
0005 | rotor | 2
... | ... | ...
I am trying to append wheel requirements to the forecast, while leaving all records in the forecast. My results would look like this:
sku | desc | fcst | wheels | wheelfcst
0001 | Car | 10000 | 4 | 40000
0002 | Boat | 5000 | |
0003 | Bike | 7500 | 2 | 15000
0004 | Shirt | 2500 | |
... | ... | ... | ... | ...
The most efficient way to go about this in my eyes is something like this query:
SELECT
t1.sku,
t1.desc,
t1.fcst,
q.quant as wheels,
t1.fcst * q.quant as wheelfcst
FROM
t1
LEFT JOIN
(
SELECT *
FROM t2
WHERE part LIKE "wheel"
)
as q
ON t1.sku = q.sku
The problem is that it gives a very elaborate Invalid Operation. error when ran.
If I remove the WHERE statement: I get the wheel parts as desired but I also pull door, hull, and rudder quantities.
If I move the WHERE statement to the main query (WHERE q.part LIKE "wheel"): I only see goods that contain wheels, but boats are then missing from the results.
I have considered a UNION statement, taking the results of the previously mentioned moving the WHERE out of the subquery (WHERE q.part LIKE "wheel"), but there doesn't seem to be a good way to grab every final item that doesn't have a wheel component because each sku can have anywhere from 0 to many components.
Is there something I'm overlooking in my desired query, or is this something requiring a UNION approach?
EDIT #1 - To answer questions raised by Andre
The full error message is Invalid operation.
sku is the primary key of t1, and there are 1426 records.
t2 contains ~446,000 records, the primary key is a composite of sku and part.
The actual WHERE statement is a partial search. All "wheels" have the same suffix but different component item numbers.
Additionally, I am in Access 2007, it may be an issue related to software version.
Making the subquery into a temporary table works, but the goal is to avoid that procedure.
EDIT #2 - A flaw in my environment
I created a test scenario identical to the one I have posted here, and I get the same results as Andre. At this point, combining these results with the fact that the temporary table method does in fact work, I am led to believe that it is an issue with query complexity and record access. Despite the error message not being the typical Query is too complex. message.
EDIT #3 - Digging deeper into "Complexity"
My next test will be to make the where clause simpler. Sadly, the systems I work on update at lunch each day and I currently cannot reach any data servers. I hope to update my progress at a later point today.
EDIT #4 - Replacing the partial search
Ok, we're back from a meeting and ready to go. I've just ran six queries with three different WHERE clauses:
WHERE part LIKE "*heel" / WHERE component_item LIKE "*SBP" (Original large scale issue)
Works in small scale test, Invalid operation on large scale.
WHERE part LIKE "wheel" / WHERE component_item LIKE "VALIDPART" (Original small scale)
Works in small scale test, Invalid operation on large scale.
WHERE part LIKE "wh33l" / WHERE component_item LIKE "NOTVALIDPART"(Where statements that do not return any records)
Small Scale
sku | desc | fcst | wheels | wheelfcst
0001 | Car | 10000 | |
0002 | Boat | 5000 | |
0003 | Bike | 10000 | |
0004 | Shirt | 5000 | |
Large Scale
sku |description |forecast |component_item |dip_rate
#####|RealItem1 | ###### | |
#####|RealItem2 | ###### | |
#####|RealItem3 | ###### | |
... |... | ... | |
Tl;dr The filter specifics did not make a difference unless the filter resulted in a subquery that returned 0 records.
EDIT #5 - An interesting result
Under the idea of trying every possible solution and test everything I can, I made a local temporary table which contained every field and every record from t2 (~25MB). Referencing this table instead of the ODBC link to t2 works with the partial search query (WHERE component_item LIKE "*SBP"). I am updating the title of this question to reflect that the issue is specific to a linked table.
I copied the sample data to Access 2010 and ran the query, it worked without problems.
What is the full error message you get? ("very elaborate Invalid Operation.")
How many records are in your tables?
Is sku primary key in t1?
In any case, I suggest changing the subquery to:
SELECT sku, quant
FROM t2
WHERE part = "wheel"
LIKE is only needed for partial searches.
A workaround
I have devised a set of queries that works by using one of my original hunches of a UNION statement. Hopefully this allows for anyone that stumbles across this issue to save a bit of time.
Setting up the UNION
My initial problem was with the idea that there was no way to select only wheel record and then join them to null records, because (using t1 as an example) the Boat has parts in t2, but none of them are wheels. I had to first devise a method to see which products had wheels without using a filter.
My intermediary solution:
Query: t1haswheel
SELECT
t1.sku,
t1.desc,
t1.fcst,
SUM(
IF(
t2.part = "wheel",
1, 0
) as haswheel
FROM
t1
LEFT JOIN
(
SELECT *
FROM t2
WHERE part LIKE "wheel"
)
as q
ON t1.sku = q.sku
GROUP BY
t1.sku,
t1.desc,
t1.fcst
This query returns every record from t1 followed by a number based on the number of wheel records there are in the part list for that item number. If there are no records in t2 with "wheel" in the field part, the query returns 0 for the record. This list is what I needed for the UNION statement in the original question.
UNION-ing it all together
At this point, all that is needed is a UNION which uses the summation field from the previous query (haswheel).
SELECT
q1.sku,
q1.desc,
q1.fcst,
t2.quant,
q1.fcst * t2.quant as wheelfcst
FROM
t1haswheel as q1
LEFT JOIN t2
ON q1.sku = t2.sku
WHERE
q1.haswheel > 0 AND
t2.part = "wheel"
UNION ALL
SELECT
q1.sku,
q1.desc,
q1.fcst,
null,
null
FROM
t1haswheel as q1
WHERE q1.haswheel = 0
This pulls in the correct results from records with wheels, and then attaches the records without wheels, while never using the WHERE statement in a subquery which references an ODBC linked table:
sku | desc | fcst | wheels | wheelfcst
0001 | Car | 10000 | 4 | 40000
0003 | Bike | 7500 | 2 | 15000
... | ... | ... | ... | ...
0002 | Boat | 5000 | |
0004 | Shirt | 2500 | |
... | ... | ... | ... | ...

SQL Server 2008 pivot query gone wrong with column name

I have some problems regarding a pivot query. I am new to this. So look for something in the internet so I found dozens of them. So I decided to follow this Link. Been practice but seems like I ran into some obvious error.
My code is:
select
risk, [Quick] AS Quick, [Brown] AS Brown, [Fox] AS Fox
from
(select risk, site
from tst) as ps
PIVOT
(
count(risk)
for site in ([Brown], [Brown], [Fox])
) AS pvt
But it is throwing an error:
Invalid column name 'risk'.
Basically I want to have an output like this:
|Foo | Quick | Brown | Fox |
| 1 | 10 | 3 | 2 |
| 2 | 5 | 4 | 4 |
| 3 | 4 | 1 | 5 |
| 4 | 2 | 3 | 7 |
| 5 | 3 | 2 | 1 |
Something like that. Just counting how many there is in a specific number. Any help would be much appreciated. Thanks
The problem with your existing query is you are using the column risk in your final select list as well as inside of the aggregate function. Once you've counted the risk values for each site this is not available to display.
To get around this you can add a second version of the risk column to your subquery similar to the following. You then count this other column of risk and display one in the final select:
select risk, [ADAB] AS ADAB, [Bahrain] AS Bahrain, [Thumrait] AS Thumrait
from
(
select risk, piv_risk = risk, site
from qcqcif
) as ps
PIVOT
(
count(piv_risk)
for site in ([ADAB], [Bahrain], [Thumrait])
) AS pvt;
See SQL Fiddle with Demo

Oracle11g, Multiple Pivots

So I have a working query that pivots some data for me.
SELECT * FROM (
select requisitions.ACC_ID AS "Accession #"
,tests.TEST_ID
,results.RESULT_NUMERIC
FROM requisitions
inner join req_panels ON requisitions.acc_id = req_panels.acc_id
inner join results ON req_panels.rp_id = results.rp_id
inner join tests ON results.test_id = tests.test_id
WHERE results.TEST_ID IN (1,2,3,4)
AND requisitions.RECEIVED_DATE > TO_DATE('9/1/2013', 'MM/DD/YYYY')
ORDER BY requisitions.ACC_ID
)
pivot(
MAX(RESULT_NUMERIC)
for TEST_ID IN ('1' AS Test1,'2' AS Test2,'3' AS Test3,'4' AS Test4)
)
Now, I have to include a different type of result (RESULTS_ALPHA in results table) as a column for each ACC_ID. RESULT_ALPHA is a clob. For the test_id's already included in the code above RESULTS_ALPHA is empty. But it holds a value for another test, we'll call it "TestAlpha".
So what I have currently output from the code above is;
Acc_ID | Test 1 | Test 2 | Test 3 | Test 4
-------------------------------------------
000001 | 24 | 1.5 | 0.5 | 2.1
000002 | 15 | 2.1 | 0.3 | 1.3
And I need to get
Acc_ID | Test 1 | Test 2 | Test 3 | Test 4 | TestAlpha
--------------------------------------------------------
000001 | 24 | 1.5 | 0.5 | 2.1 | abcd
000002 | 15 | 2.1 | 0.3 | 1.3 | efgh
How can I accomplish this? Another pivot?
Thanks.
If you can use just the first 4000 characters of a CLOB field then you can just substring it:
SELECT * FROM (
select requisitions.ACC_ID AS "Accession #"
,tests.TEST_ID
,results.RESULT_NUMERIC
,dbms_lob.substr(results.RESULT_ALPHA, 4000, 1) as result_alpha
FROM requisitions
...
Of course that gives you a 4000-character-wide column in your output, but not much you can do about that really, unless you can set a lower length based on knowledge of what's in the column. (Though if it's less than 4K, it doesn't really make sense to store it as a CLOB; sounds like you have a mix of data in there though).
Even if the value is more than 4000 characters this will show the start of it. Whether that's acceptable, or useful, depends what you're doing with the result of the pivot.
What you're doing does seem to assume that the RESULTS_ALPHA is the same for all results records for each TEST_ID; or even for each ACC_ID. Which seems a bit wasteful if it's true.
I'm not sure if there a non-programmatic solution to get the full CLOB back.

How to get numbers arranged right to left in sql server SELECT statements

When performing SELECT statements including number columns (prices, for example), the result always is left to right ordered, which reduces the readability. Therefore I'm searching a method to format the output of number columns right to left.
I already tried to use something like
SELECT ... SPACE(15-LEN(A.Nummer))+A.Nummer ...
FROM Artikel AS A ...
which gives close results, but depending on font not really. An alternative would be to replace 'SPACE()' with 'REPLICATE('_',...)', but I don't really like the underscores in output.
Beside that this formula will crash on numbers with more digits than 15, therefore I searched for a way finding the maximum length of entries to make it more save like
SELECT ... SPACE(MAX(A.Nummer)-LEN(A.Nummer))+A.Nummer ...
FROM Artikel AS A ...
but this does not work due to the aggregate character of the MAX-function.
So, what's the best way to achieve the right-justified order for the number-columns?
Thanks,
Rainer
To get you problem with the list box solved have a look at this link: http://www.lebans.com/List_Combo.htm
I strongly believe that this type of adjustment should be made in the UI layer and not mixed in with data retrieval.
But to answer your original question i have created a SQL Fiddle:
MS SQL Server 2008 Schema Setup:
CREATE TABLE dbo.some_numbers(n INT);
Create some example data:
INSERT INTO dbo.some_numbers
SELECT CHECKSUM(NEWID())
FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))X(x);
The following query is using the OVER() clause to specify that the MAX() is to be applied over all rows. The > and < that the result is wrapped in is just for illustration purposes and not required for the solution.
Query 1:
SELECT '>'+
SPACE(MAX(LEN(CAST(n AS VARCHAR(MAX))))OVER()-LEN(CAST(n AS VARCHAR(MAX))))+
CAST(n AS VARCHAR(MAX))+
'<'
FROM dbo.some_numbers SN;
Results:
| COLUMN_0 |
|---------------|
| >-1486993739< |
| > 1620287540< |
| >-1451542215< |
| >-1257364471< |
| > -819471559< |
| >-1364318127< |
| >-1190313739< |
| > 1682890896< |
| >-1050938840< |
| > 484064148< |
This query does a straight case to show the difference:
Query 2:
SELECT '>'+CAST(n AS VARCHAR(MAX))+'<'
FROM dbo.some_numbers SN;
Results:
| COLUMN_0 |
|---------------|
| >-1486993739< |
| >1620287540< |
| >-1451542215< |
| >-1257364471< |
| >-819471559< |
| >-1364318127< |
| >-1190313739< |
| >1682890896< |
| >-1050938840< |
| >484064148< |
With this query you still need to change the display font to a monospaced font like COURIER NEW. Otherwise, as you have noticed, the result is still misaligned.