I am looking at creating a lookup table to join with one of our existing tables. The strucuture of the existing table is as follows:
Version| CompanyNumber|EffDate |ExpDate |Indicator
------------------------------------------------------
1 | 2 |xx/xx/xxxx|xx/xx/xxxx| 0
2 | 2 |xx/xx/xxxx|xx/xx/xxxx| 1
The new table has the structure of this and should be populated like so:
ID | Version | Form
---------------------
1 | 1 | 1
2 | 1 | 2
3 | 1 | 3
4 | 2 | 3
What I am struggling with is populating the new table with the data in the example above. If the indicator is 0 I will always populate the form with 1, 2 and 3 for the version.
So if the indicator is 0 I want to add form 1, 2, and 3 for each version and if the indicator is 1 I only want to add form 3.
Thanks in Advance
You can use a query like this to perform INSERT:
INSERT INTO Table2(Version, Form)
SELECT Version, x.v
FROM Table1
INNER JOIN (VALUES (1, 3), (2, 2), (3, 1)) AS x(i, v)
ON IIF(Table1.Indicator = 0, 3, Table1.Indicator) >= x.i
If Indicator is equal to 0, then 3 rows are being inserted, otherwise only 1 row is inserted.
Note: I assume that field ID of Table2 is an IDENTITY field.
Related
Introduction:
I have come across an unexpected challenge. I'm hoping someone can help and I am interested in the best method to go about manipulating the data in accordance to this problem.
Scenario:
I need to combine column data associated to two different ID columns. Each row that I have associates an item_id and the quantity for this item_id. Please see below for an example.
+-------+-------+-------+---+
|cust_id|pack_id|item_id|qty|
+-------+-------+-------+---+
| 1 | A | 1 | 1 |
| 1 | A | 2 | 1 |
| 1 | A | 3 | 4 |
| 1 | A | 4 | 0 |
| 1 | A | 5 | 0 |
+-------+-------+-------+---+
I need to manipulate the data shown above so that 24 rows (for 24 item_ids) is combined into a single row. In the example above I have chosen 5 items to make things easier. The selection format I wish to get, assuming 5 item_ids, can be seen below.
+---------+---------+---+---+---+---+---+
| cust_id | pack_id | 1 | 2 | 3 | 4 | 5 |
+---------+---------+---+---+---+---+---+
| 1 | A | 1 | 1 | 4 | 0 | 0 |
+---------+---------+---+---+---+---+---+
However, here's the condition that is making this troublesome. The maximum total quantity for each row must not exceed 5. If the total quantity exceeds 5 a new row associated to the cust_id and pack_id must be created for the rest of the item_id quantities. Please see below for the desired output.
+---------+---------+---+---+---+---+---+
| cust_id | pack_id | 1 | 2 | 3 | 4 | 5 |
+---------+---------+---+---+---+---+---+
| 1 | A | 1 | 1 | 3 | 0 | 0 |
| 1 | A | 0 | 0 | 1 | 0 | 0 |
+---------+---------+---+---+---+---+---+
Notice how the quantities of item_ids 1, 2 and 3 summed together equal 6. This exceeds the maximum total quantity of 5 for each row. For the second row the difference is created. In this case only item_id 3 has a single quantity remaining.
Note, if a 2nd row needs to be created that total quantity displayed in that row also cannot exceed 5. There is a known item_id limit of 24. But, there is no known limit of the quantity associated for each item_id.
Here's an approach which goes from left-field a bit.
One approach would have been to do a recursive CTE, building the rows one-by-one.
Instead, I've taken an approach where I
Create a new (virtual) table with 1 row per item (so if there are 6 items, there will be 6 rows)
Group those items into groups of 5 (I've called these rn_batches)
Pivot those (based on counts per item per rn_batch)
For these, processing is relatively simple
Creating one row per item is done using INNER JOIN to a numbers table with n <= the relevant quantity.
The grouping then just assigns rn_batch = 1 for the first 5 items, rn_batch = 2 for the next 5 items, etc - until there are no more items left for that order (based on cust_id/pack_id).
Here is the code
/* Data setup */
CREATE TABLE #Order (cust_id int, pack_id varchar(1), item_id int, qty int, PRIMARY KEY (cust_id, pack_id, item_id))
INSERT INTO #Order (cust_id, pack_id, item_id, qty) VALUES
(1, 'A', 1, 1),
(1, 'A', 2, 1),
(1, 'A', 3, 4),
(1, 'A', 4, 0),
(1, 'A', 5, 0);
/* Pivot results */
WITH Nums(n) AS
(SELECT (c * 100) + (b * 10) + (a) + 1 AS n
FROM (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) A(a)
CROSS JOIN (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) B(b)
CROSS JOIN (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) C(c)
),
ItemBatches AS
(SELECT cust_id, pack_id, item_id,
FLOOR((ROW_NUMBER() OVER (PARTITION BY cust_id, pack_id ORDER BY item_id, N.n)-1) / 5) + 1 AS rn_batch
FROM #Order O
INNER JOIN Nums N ON N.n <= O.qty
)
SELECT *
FROM (SELECT cust_id, pack_id, rn_batch, 'Item_' + LTRIM(STR(item_id)) AS item_desc
FROM ItemBatches
) src
PIVOT
(COUNT(item_desc) FOR item_desc IN ([Item_1], [Item_2], [Item_3], [Item_4], [Item_5])) pvt
ORDER BY cust_id, pack_id, rn_batch;
And here are results
cust_id pack_id rn_batch Item_1 Item_2 Item_3 Item_4 Item_5
1 A 1 1 1 3 0 0
1 A 2 0 0 1 0 0
Here's a db<>fiddle with
additional data in the #Orders table
the answer above, and also the processing with each step separated.
Notes
This approach (with the virtual numbers table) assumes a maximum of 1,000 for a given item in an order. If you need more, you can easily extend that numbers table by adding additional CROSS JOINs.
While I am in awe of the coders who made SQL Server and how it determines execution plans in millisends, for larger datasets I give SQL Server 0 chance to accurately predict how many rows will be in each step. As such, for performance, it may work better to split the code up into parts (including temp tables) similar to the db<>fiddle example.
I am currently trying to manipulate a table in SQL Server.
The following table is an example:
Id | PrimaryId | PrimaryMail | SecondaryId | SecondaryMail
1. 1. email1#something. 5. email2#something
2. 2. email3#something. 6. email4#something
3. 3. email5#something. 7. email6#something
4. 4. email7#something. 8. email8#something
I want it to become the following:
Id | PrimaryId | PrimaryMail | SecondaryId | SecondaryMail
1. 1. email1#something. 6. email4#something
2. 2. email3#something. 7. email6#something
3. 3. email5#something. 8. email8#something
4. 4. email7#something. 5. email2#something
I found a similar question on here but the person did not want to use joins and the actual thing I am after is the rollover mechanic that makes either the first row become the last row or the last row the first row depending on what direction you shift the columns.
I already have the following code that makes the items in the columns go up by 1 row. But the last row currently stays the same and I lose the data from the first row.
--Calculate the height of the table
DECLARE #CouplesCount AS INT
SELECT #CouplesCount = COUNT(*) FROM Couples
--Shift the Secondary users down one row
UPDATE c1
SET c1.SecondaryId = c2.SecondaryId, c1.SecondaryMail = c2.SecondaryMail
FROM Couples c1 join
Couples c2
on c2.Id = c1.Id + 1
WHERE c1.CouplesId <= #CouplesCount - 1
SELECT * FROM Couples
Thank in advance to anyone that can help me.
p.s. I cannot use MySQL code
If the version of SQL Server that you use is SQL Server 2012 or later, you can use window functions LEAD and FIRST_VALUE.
LEAD returns the values from the "next" row (as defined by the ORDER BY clause). It will return NULL for the last row, because there is no "next" row there.
So, for the last row we need to return the first row, which can be done by the FIRST_VALUE.
Sample data
DECLARE #Couples TABLE (
Id int, PrimaryId int, PrimaryMail nvarchar(100),
SecondaryId int, SecondaryMail nvarchar(100));
INSERT INTO #Couples VALUES
(1, 1, 'email1#something', 5, 'email2#something'),
(2, 2, 'email3#something', 6, 'email4#something'),
(3, 3, 'email5#something', 7, 'email6#something'),
(4, 4, 'email7#something', 8, 'email8#something');
Query
SELECT
C.Id
,C.PrimaryId
,C.PrimaryMail
,ISNULL(LEAD(C.SecondaryId) OVER (ORDER BY C.Id),
FIRST_VALUE(C.SecondaryId) OVER (ORDER BY C.Id))
AS SecondaryId
,ISNULL(LEAD(C.SecondaryMail) OVER (ORDER BY C.Id),
FIRST_VALUE(C.SecondaryMail) OVER (ORDER BY C.Id))
AS SecondaryMail
FROM
#Couples AS C
;
Result
+----+-----------+------------------+-------------+------------------+
| Id | PrimaryId | PrimaryMail | SecondaryId | SecondaryMail |
+----+-----------+------------------+-------------+------------------+
| 1 | 1 | email1#something | 6 | email4#something |
| 2 | 2 | email3#something | 7 | email6#something |
| 3 | 3 | email5#something | 8 | email8#something |
| 4 | 4 | email7#something | 5 | email2#something |
+----+-----------+------------------+-------------+------------------+
Update
If you need to actually change the table itself, it is easy to do as well, just wrap the query in a CTE and update it.
WITH
CTE
AS
(
SELECT
C.Id
,C.PrimaryId
,C.PrimaryMail
,C.SecondaryId
,C.SecondaryMail
,ISNULL(LEAD(C.SecondaryId) OVER (ORDER BY C.Id),
FIRST_VALUE(C.SecondaryId) OVER (ORDER BY C.Id))
AS NewSecondaryId
,ISNULL(LEAD(C.SecondaryMail) OVER (ORDER BY C.Id),
FIRST_VALUE(C.SecondaryMail) OVER (ORDER BY C.Id))
AS NewSecondaryMail
FROM
#Couples AS C
)
UPDATE CTE
SET
SecondaryId = NewSecondaryId
,SecondaryMail = NewSecondaryMail
;
SELECT * FROM #Couples ORDER BY Id;
I have two tables, where table A has to be Updated or insert a row base on existing. I tried this by using JOINS EXCEPT and MERGE statement but I have one problem i can't solve. so here is an example :
Table A (Attribut-Table)
attr | attrValue | prodID
--------------------------
4 | 2 | 1
--------------------------
3 | 10 | 2
--------------------------
1 | 7 | 2
--------------------------
3 | 10 | 3
--------------------------
6 | 9 | 3
--------------------------
1 | 4 | 3
--------------------------
Table P(Product-Table)
prodID | stock |
------------------
1 | 1
------------------
2 | 0
------------------
3 | 1
------------------
4 | 1
------------------
Now what i would like to do the following in SQL:
All products, that has Stock > 0 should have an entry in Table A with attr = 6 and attrValue = 9
All products, that has Stock < 1 should have an entry in Table A with attr = 6 and attrValue = 8
i need a SQL Query to do that because my problem is that there are multiple entries for a prodID in Table A
That is what i am thinking of:
Fist check if any entry for the prodID(in Table B) exist in Table A, if not INSERT INTO Table A ( attr=6 and, attrValue = 8/9 (depends on Stock), prodID
If there is already an entry for the prodID in Table A with the attr = 6, then Update this row and set attrValue to 8/9 (depending on stock)
so I am looking for a translation of "my thoughts" to a sqlQuery
thanks for helping.
(using: SQL SERVER Express 2012 and HEIDI SQL for management)
Since your "attr 6" row is 100 % derivable from the state of the P table, it is a poor idea to store that row redundantly in A.
This is better :
(1) Define a first view ATTR6_FOR_P as SELECT prodID, 6 as attr, CASE (...) as attrValue from P. The CASE expression chooses the value 8 or 9 according to stock value in P.
(2) Define a second view A_EXT as A UNION ATTR6_FOR_P. (***)
Now changes in stock will always immediately be reflected in A_EXT without having to update explicitly.
(***) but beware of column ordering because SQL UNION does not match columns by name but by ordinal position instead.
Table "public.t"
Column | Type | Modifiers
--------+---------+-----------
code | text |
grid | integer |
The codigo column, although of type text, has a numeric sequence which has
duplicates. The grid column is a unique sequence.
select * from t order by grid;
code | grid
------+------
1 | 1
1 | 2
1 | 3
2 | 4
2 | 5
2 | 6
3 | 7
The goal is to eliminate the duplicates in the code column to make it unique. The result should be similar to:
code | grid
------+------
1 | 1
6 | 2
4 | 3
2 | 4
7 | 5
5 | 6
3 | 7
The version is 8.2 (no window functions).
create table t (code text, grid integer);
insert into t values
('1',1),
('1',2),
('1',3),
('2',4),
('2',6),
('3',7),
('2',5);
This is the solution that worked.
drop sequence if exists s;
create temporary sequence s;
select setval('s', (select max(cast(code as integer)) m from t));
update t
set code = i
from (
select code, grid, nextval('s') i
from (
select code, max(grid) grid
from t
group by code
having count(*) > 1
order by grid
) q
) s
where
t.code = s.code
and
t.grid = s.grid
The problem with it is that the update command must be repeated until there are no more duplicates. It is just a "it is not perfect" problem as it is a one time operation only.
Export (and remove) everything but code column (maybe you could subquery for export, and remove just duplicated row). Make code primary with something such auto increment behaviour and reimport everything. code column should be automatically generated.
i have a table like this one:
--------------------------------
id | name
--------------------------------
1 | aa
2 | aa
3 | aa
4 | aa
5 | bb
6 | bb
... one million more ...
and i like to obtain an arbitrary number of rows in a pre defined sequence and the other rows ordered by their name. e.g. in another table i have a short sequence with 3 id's:
sequ_no | id | pos
-----------------------
1 | 3 | 0
1 | 1 | 1
1 | 2 | 2
2 | 65535 | 0
2 | 45 | 1
... one million more ...
sequence 1 defines the following series of id's: [ 3, 1, 2]. how to obtain the three rows of the first table in this order and the rest of the rows ordered by their name asc?
how in PostgreSQL and how in mySQL? how would a solution look like in hql (hibernate query language)?
an idea i have is to first query and sort the rows which are defined in the sequence and than concat the other rows which are not in the sequence. but this involves tow queries, can it be done with one?
Update: The final result for the sample sequence [ 3, 1, 2](as defined above) should look like this:
id | name
----------------------------------
3 | aa
1 | aa
2 | aa
4 | aa
5 | bb
6 | bb
... one million more ...
i need this query to create a pagination through a product table where part of the squence of products is a defined sequence and the rest of the products will be ordered by a clause i dont know yet.
I'm not sure I understand the exact requirement, but won't this work:
SELECT ids.id, ids.name
FROM ids_table ids LEFT OUTER JOIN sequences_table seq
WHERE ids.id = seq.id
ORDER BY seq.sequ_no, seq.pos, ids.name, ids.id
One way: assign a position (e.g. 0) to each id that doesn't have a position yet, UNION the result with the second table, join the result with the first table, and ORDER BY seq_no, pos, name.