How can I generate sql matrix - sql

How I can create this matrix from the tables in sql query? I have table which show how many goods was sold. I want show title of goods instead id of goods. But it's important that fields toy1, toy2 and toy3 have datatype decimal(20,8)
Table 1
id year toy1 toy2 toy3
-------------------------------
-1 1 3 2
1 2016 100 20 50
2 2017 150 120 40
Table 2
id toy
---------------
1 doll
2 car
3 bear
Result matrix
id year toy1 toy2 toy3
-------------------------------
-1 doll bear car
1 2016 100 20 50
2 2017 150 120 40

For SQL Server i will do some left joins
SELECT T.id,
T.year,
CASE
WHEN T1.TOY IS NULL THEN CAST(T.[toy1] AS VARCHAR) ELSE T1.TOY
END [toy1],
CASE
WHEN T2.TOY IS NULL THEN CAST(T.[toy2] AS VARCHAR) ELSE T2.TOY
END [toy2],
CASE
WHEN T3.TOY IS NULL THEN CAST(T.[toy3] AS VARCHAR) ELSE T3.TOY
END [toy3]
FROM <Table1> T
LEFT JOIN <Table2> T1 ON T1.ID = T.toy1
LEFT JOIN <Table2> T2 ON T2.ID = T.toy2
LEFT JOIN <Table2> T3 ON T3.ID = T.toy3;
Result :
id year toy1 toy2 toy3
-1 NULL doll bear car
1 2016 100 20 50
2 2017 150 120 40

This is a very bad data model.
A table should represent an entity, such as a toy, an order, yearly sales, ... A column on the other side represents an attribute of that entity, e.g. the toy's name or price, the order's date, the sale's year. In your model you are mixing this. What is "toy1"? A toy ID? An amount? A mix of both it seems. Then your table 2 allows for many, many toys, whereas your table1 allows for only 3. That doesn't match.
What you should have instead:
table toys
toy_id | name
-------+-----
1 | doll
2 | car
3 | bear
table sales
year | toy_id | amount
2016 | 1 | 100
2016 | 2 | 20
2016 | 3 | 50
2017 | 1 | 150
2017 | 2 | 120
2017 | 3 | 40
And you wouldn't write a query to give you the matrix either. You would select the data (e.g. select * from sales order by year, toy_id) and have your app handle the display. It is easy to fetch the data in a loop with whatever programming language, be it C#, Visual Basic, Java, PHP, whatever, and display it in a grid.
In an SQL query you must know the columns beforehand, but this is not the case with your matrix output, as you would have to know how many toys are in the toys table before running or even writing the query. Everytime you added a toy, you'd have to alter your query. This is not how relational databases are supposed to work.

I am not sure but something like this:
SELECT T1.id,
year,
CASE WHEN T1.Toy1 = T2.id AND T1.id=-1 THEN T2.toy
ELSE T1.Toy1
END AS Toy1,
CASE WHEN T1.Toy2 = T2.id AND T1.id=-1THEN T2.toy
ELSE T1.Toy2
END AS Toy1,
CASE WHEN T1.Toy3 = T2.id AND T1.id=-1 THEN T2.toy
ELSE T1.Toy3
END AS Toy3
FROM Table1 T1
LEFT JOIN Table2 T2 ON T1.id=-1 AND T2.id IN (T1.Toy1, T1.Toy2, T1.Toy3)

One way is to only select the -1 row and apply the algorithms to get the toy names. That can be UNION ALL with the rest of the table that is not the -1 row.
You will have to Cast the output as varchar to mix words and numbers:
DECLARE #table1 TABLE (id int, [year] int, toy1 int, toy2 int, toy3 int)
DECLARE #table2 TABLE (id int, toy varchar(100))
INSERT INTO #table1 VALUES (-1, null, 1, 3, 2), (1, 2016, 100,20,50), (2,2017,150,120,40)
INSERT INTO #table2 VALUES (1,'doll'), (2,'car'), (3,'bear')
-- for the -1 row
SELECT T1.id, COALESCE(CAST(year as varchar), '') AS year
,(SELECT toy FROM #table2 T2 WHERE T2.id = T1.toy1) AS toy1
,(SELECT toy FROM #table2 T2 WHERE T2.id = T1.toy2) AS toy2
,(SELECT toy FROM #table2 T2 WHERE T2.id = T1.toy3) AS toy3
FROM #table1 T1
WHERE T1.id = -1
UNION ALL
--all other rows
SELECT T3.id
,CAST(T3.year as varchar)
,CAST(T3.toy1 as varchar)
,CAST(T3.toy2 as varchar)
,CAST(T3.toy3 as varchar)
FROM #table1 T3
WHERE T3.id <> -1
Gives output:
id year toy1 toy2 toy3
-1 doll bear car
1 2016 100 20 50
2 2017 150 120 40

Related

Oralce SQL nested or inner join when you need to compare the same table but different rows with unique ID values

I'm having a trouble writing a query in ORACLE. I have a Table that contains values. for example:
ID quantity partID
123 50 10
100 20 10
100 30 11
123 null 8
456 null 100
789 25 123
456 50 9
I want to get all rows that has same ID but quantities to be 50 and null (exact same pairs of 50 and null only). for the given example I would like to get:
ID quantity partID
123 50 10
123 null 8
456 50 9
456 null 100
I tried inner join but it doesn't provide the exact output as expected.
You may try :
select ID, quantity, partID
from tab
where ID in
(
select ID
from tab
where nvl(quantity,50)=50
group by ID
having count(distinct nvl(quantity,0) )>1
);
ID QUANTITY PARTID
123 50 10
123 (null) 8
456 (null) 100
456 50 9
SQL Fiddle Demo
P.S. you may get the same results by commenting out having count(ID)=2 also but for those cases there may not exist one of 50 or null for values of quantity.
You can use exists:
select t.*
from t
where (t.quantity = 50 and
exists (select 1 from t t2 where t2.id = t.id and t2.partid = t.partid and t2.value is null)
) or
(t.quantity is null and
exists (select 1 from t t2 where t2.id = t.id and t2.partid = t.partid and t2.value = 50)
) ;

Update a null value cell with an ID and the table is related to 3 others

I have a Table that is joined from other tables. I want to update a null value in this Table by a specific number series. Below is the illustration:
The code for the Table which is called List_Codes
SELECT mlk.MLK_CODE
FROM zpt
LEFT OUTER JOIN mes ON mes.ZPT_ID = zpt.ZPT_ID
LEFT OUTER JOIN zmlk ON zpt.ZPT_ID = zmlk.ZPT_ID
LEFT OUTER JOIN mlk ON zmlk.MLK_ID = mlk.MLK_ID
WHERE zpt.zpt_id IS NOT NULL
and zpt.zpt_meteringcode = '123456'
ORDER BY mes.MES_STATUS DESC
Now I want to update this specific row's mlk.MLK_CODE from null to '789'. I have located this row based on the zpt.zpt_meteringcode. Any suggestions plz?
The tables look like this, and the List_Code Table is the result of the above code
Mlk Table
Mlk_id Mlk_code
1 123
2 456
Zpt Table
Zpt_id Zpt_meteringcode
10 123456
20 987654
30 654321
40 147852
Zmlk Table
Zpt_id Mlk_id
20 1
30 2
List_Code Table
Zpt_id Zpt_meteringcode Mlk_id Mlk_code
10 123456
20 987654 1 123
30 654321 2 456
40 147852
I think you need two inserts and one update statements like this.
INSERT INTO mlk (mlk_code)
VALUES ( '789' ); -- Primary key is generated
.....
INSERT INTO zmlk (zpt_id,
mlk_id)
SELECT (SELECT zpt_id
FROM zpt
WHERE zpt_meteringcode = '123456'),
(SELECT mlk_id
FROM mlk
WHERE mlk_code = '789')
FROM dual;
....
UPDATE list_code
SET ( mlk_id, mlk_code ) = (SELECT mlk_id,
mlk_code
FROM mlk
WHERE mlk_code = '789')
WHERE zpt_meteringcode = '123456';

SQL - Find the Top Level Parent and Multiply Quantities

I have two tables which track part numbers as well as the hierarchy of assemblies.
Table: Config
ConfigNum AssemblyNum Qty
1 A 1
1 B 2
1 C 2
2 A 1
2 C 1
Table: SubAssembly
SubAssembly PartNum Qty
A AA 2
A BB 4
A CC 2
A DD 4
B EE 4
B FF 8
AA AAA 2
I would like to create a flat version of these tables which shows the ConfigNum (Top level parent) with all associated assembly and part numbers, for each ConfigNum in the Config table. The column Config.AssemblyNum is equivalent to SubAssembly.SubAssembly.
The Subassembly table shows the partent to child relation ship between parts. For example Assembly 'A' has a child assembly 'AA'. Since 'AA' exists in the SubAssembly Column is it self an assembly and as you can see it has a child part 'AAA'. 'AAA' does not exist in the SubAssemly columns therefore it is the last child in the series.
I would also like to have an accurate quantity count of each part based on multiplying of parent to child down the chain.
For example in the output:
(Total Qty of AAA) = (Qty A) x (Qty AA) x (Qty AAA)
4 = 1 x 2 x 2
Example Output table: (for Config 1)
ConfigNum SubAssembly PartNum TotalQty
1 A AA 2
1 A BB 4
1 A CC 2
1 A DD 4
1 B EE 8
1 B FF 16
1 A AAA 4
Any suggestion on how to complete this task would be greatly appreciated.
EDIT: I have been able to create this code based on suggestions made in the answers.
I am still having trouble getting the quantities to multiply down.
I have received the error "Types don't match between the anchor and the recursive part in column "PartQty" of recursive query "RCTE"."
;WITH RCTE (AssemblyNum, PartNum, PartQty, Lvl) AS
(
SELECT AssemblyNum, PartNum, PartQty, 1 AS Lvl
FROM SP_SubAssembly r1
WHERE EXISTS (SELECT * FROM SP_SubAssembly r2 WHERE r1.AssemblyNum = r2.PartNum)
UNION ALL
SELECT rh.AssemblyNum, rc.PartNum, (rc.PartQty * rh.PartQty), Lvl+1 AS Lvl
FROM dbo.SP_SubAssembly rh
INNER JOIN RCTE rc ON rh.PartNum = rc.AssemblyNum
)
SELECT CB.ID, CB.ConfigNum, CB.PartNum, CB.PartQty, r.AssemblyNum, r.PartNum, SUM(r.PartQty * COALESCE(CB.PartQty,1)) AS TotalQty
FROM SP_ConfigBOM CB
FULL OUTER JOIN RCTE r ON CB.PartNum = r.AssemblyNum
WHERE CB.ConfigNum IS NOT NULL
ORDER BY CB.ConfigNum
Thanks,
For this problem I think you must use a recursive query. In fact I think SubAssembly table should have some ProductID field other than SubAssembly to easily identify the main product that contains assemblies.
You can find a similar example in SLQ Server documentation.
Can check it here: http://rextester.com/FQYI80157
Change the Qty in Config table to change the final result.
create temp table t1 (cfg int, part varchar(10), qty int);
create temp table t2 (part varchar(10), sasm varchar(10), qty int);
insert into t1 values (1,'A',2);
insert into t2 values ('A','AA',2);
insert into t2 values ('A','BB',4);
insert into t2 values ('A','CC',2);
insert into t2 values ('A','DD',4);
insert into t2 values ('AA','AAA',2);
WITH cte(sasm, part, qty)
AS (
SELECT sasm, part, qty
FROM #t2 WHERE part = 'A'
UNION ALL
SELECT p.sasm, p.part, p.qty * pr.qty
FROM cte pr, #t2 p
WHERE p.part = pr.sasm
)
SELECT #t1.cfg, cte.part, cte.sasm, SUM(cte.qty * COALESCE(#t1.qty,1)) as total_quantity
FROM cte
left join #t1 on cte.part = #t1.part
GROUP BY #t1.cfg, cte.part, cte.sasm;
This is the result:
+------+------+----------------+
| part | sasm | total_quantity |
+------+------+----------------+
| A | AA | 4 |
+------+------+----------------+
| A | DD | 8 |
+------+------+----------------+
| AA | AAA | 4 |
+------+------+----------------+
| A | BB | 8 |
+------+------+----------------+
| A | CC | 4 |
+------+------+----------------+
insert into #Temp
SELECT A.[ConfigNum] ,
A.[AssemblyNum],
B.[PartNum],
A.[Qty],
A.QTY * B.Qty TotalQty
INTO #Temp
FROM [Config] A,
[SubAssembly] B
WHERE A.[AssemblyNum] = B.[SubAssembly]
SELECT A.[ConfigNum] ,
A.[AssemblyNum],
A.[PartNum],
A.[Qty],
A.TotalQty
FROM #Temp A
union
SELECT A.[ConfigNum] ,
A.[AssemblyNum],
B.[PartNum],
A.[Qty],
A.TotalQty * B.Qty
FROM #Temp A,
[SubAssembly] B
WHERE
A.[PartNum] = B.[SubAssembly]

SQL - Join tables where first table has a value that must fall within the range specified in the second

I've got a resultset that looks something like:
[DateField ][Hour][Value]
2014-10-01 1 200
2014-10-01 2 600
I need to add a couple of additional columns from another table like:
[DateField ][Hour][Value][T2Value1][T2Value2]
2014-10-01 1 200 Off 5
2014-10-01 2 600 Off 7
I need the T2Value1 and T2Value2 from Table 2 below if the Value field from Table 1 falls between the RangeValue1 and RangeValue2 of Table 2.
[Id][T2Value1][T2Value2][RangeValue1][RangeValue2]
1 Off 5 1 500
2 Off 7 501 1000
I've started to query
select datefield, hour, value, t2value1, t2value2 from
(
-- inner query that returns datefield, hour, value
) Table1, Table2
but don't know where to take it from here. Would appreciate any help, thanks!
See this simple example:
declare #Values as table
(
Name varchar(10) not null
,Value int not null
)
declare #Ranges as table
(
Grade char(1) not null
,v0 int not null
,v1 int not null
)
insert into #Values
values
('Albert', 33)
,('Bob', 133)
,('Carl', 233)
insert into #Ranges
values
('A',0,100)
,('B',100,200)
,('C',200,300)
select * from #Values v
join #Ranges r on r.v0 <= v.Value and v.Value < r.v1
As you can see it's ok to put some more complex condition on the join clause, in this case a range condition
it results in:
Name Value Grade v0 v1
---------- ----------- ----- ----------- -----------
Albert 33 A 0 100
Bob 133 B 100 200
Carl 233 C 200 300
A simple join should get you what you need. The on condition is a little more complicated than normal, but not much.
select t1.DateField, t1.Hour, t2.t2value1, t2.t2value2
from table1 t1
inner join table2 t2 on t2.RangeValue1 <= t1.Value and t1.Value <= t2.RangeValue2

SQL Always return all rows from LUT for each ID

I am wondering if this query can be modified to achieve what I want:
SELECT
cv.[ID]
,cv.[CustomValue]
,cf.[SpecialInformationId]
FROM #CustomFields cf
FULL OUTER JOIN #CustomValues cv ON cf.SpecialInformationId = cv.id
This returns all cv.Id's. It also returns any unmatched cf.SpecialInformationId's with a NULL for the cv information. What I actually want is for each instance of cv, I want every cf to show. cf is a lookup table. In this instance there are 12 values, but that varies everytime the query runs. Here is an example:
What this query currently returns:
cv.id cv.customvalue cf.specialinformationid
1 003 1
1 abc 2
2 004 1
2 1/1/2010 4
2 abc 2
3 009 1
4 003 1
4 acb 2
4 1/2/2010 4
What I want it to return:
cv.id cv.customvalue cf.specialinformationid
1 003 1
1 abc 2
1 NULL 3
1 NULL 4
1 NULL 5
2 004 1
2 abc 2
2 NULL 3
2 1/1/2010 4
2 NULL 5
3 009 1
3 NULL 2
3 NULL 3
3 NULL 4
3 NULL 5
4 003 1
4 acb 2
4 NULL 3
4 1/2/2010 4
4 NULL 5
A Left join cannot be used because there are only 12 rows in the lookup table so if a left join is used the same result will be achieved as the full outer join.
This is a spinoff of my other question:
SQL 2 tables always return all rows from 1 table match existing on other
Thanks
I believe a CROSS JOIN will achieve the results you're looking for.
The problems are arising because your Table2 is not really a 'vehicle' table. Because the VehicleId does not uniquely identify a record in that table. This is where all of the confusion is coming from. So to solve that and get your problem to work I did a select distinct on table2 against the values in table 1 (I also did a select distinct for clarity, but it was not necessary.) Hope this helps.
CREATE TABLE #Table1 (Id INT)
CREATE TABLE #Table2 (VehicleID INT, Value VARCHAR(50), Table1ID INT)
INSERT INTO #Table1 VALUES (1),(2),(3),(4),(5)
INSERT INTO #Table2 VALUES (1, 't', 1),(1, 'q', 2),(3, 'w', 3),(3, 'e', 4),(4, 't', 1),(5, 'e', 1),(5, 'f', 2),(5, 'g', 4)
SELECT * FROM #Table1
SELECT * FROM #Table2
SELECT t2.VehicleID, t2.Value
FROM ( SELECT t2.VehicleId, t1.Id
FROM ( SELECT DISTINCT
VehicleId
FROM #Table2 ) t2
CROSS JOIN ( SELECT Id
FROM #Table1 ) t1 ) Base
LEFT JOIN #Table2 t2
ON Base.VehicleId = t2.VehicleID
AND Base.Id = t2.Table1ID
WHERE (Base.VehicleId BETWEEN 1 AND 3)
DROP TABLE #Table1
DROP TABLE #Table2