Now i have the following data which is a result of a pivot table:
ID | Level 1 | Level 2 | Level 3
01 | 5 | 4 | 0
02 | 1 | 0 | 6
03 | 8 | 3 | 1
Can i visualise this information on a stacked chart in qlikview with X-Axis showing Level 1, Level 2, Level 3? The vertical axis will show the values in each of the level. Each bar will be stacked with the different IDs in different colors. I want to keep the dataset to be in this format.
Create a bar chart; have two dimensions (The Level-dimension and ID). Use the same expression. In the tab Style select "Stacked" as Subtype.
I used sum(Value) with the following test data.
data:
load * inline [
ID, Dim, Value
1, Level1, 5
1, Level2, 4
1, Level3, 0
2, Level1, 1
2, Level2, 0
2, Level3, 6
3, Level1, 8
3, Level2, 3
3, Level3, 1
];
Related
I have the following table:
>>> id crop grower loc
0 11 maize Lulu Fiksi
1 13 maize Lulu Menter
2 05 maize Felix Hausbauch
3 04 apples Lulu Fiksi
4 02 apples Meni Linter
5 06 cotton Delina Marchi
6 12 cotton Lexi Tinta
7 16 cotton Lexi Ferta
...
I want tto create new table which will show the unique crop names, count of the crops appearence and then list of all the growers that grow this crop,so the result table should look like this:
>>> crop total_count growers
0 maize 3 Lulu, Felix
1 apples 2 Lulu,Meni
2 cotton 3 Delina, Lexi
I manage to create table that shows the crops and the total count without the growers names:
select "CROP",count(*) "totalCount"
from "table"
group by "CROP"
order by "totalCount" desc
My question is how can I create new table with new column that contains list of unique growers for each crop (like in the example).
GROUP_CONCAT is for MySQL, Snowflake uses LISTAGG:
create or replace table test (
id int,
crop varchar,
grower varchar,
loc varchar
);
insert into test values
(11, 'maize', 'Lulu', 'Fiksi'),
(13, 'maize', 'Lulu', 'Menter'),
(5, 'maize', 'Felix', 'Hausbauch'),
(4, 'apples', 'Lulu', 'Fiksi'),
(2, 'apples', 'Meni', 'Linter'),
(6, 'cotton', 'Delina', 'Marchi'),
(12, 'cotton', 'Lexi', 'Tinta'),
(16, 'cotton', 'Lexi', 'Ferta');
select
crop,
count(1) as total_count,
listagg(distinct grower, ', ') as growers
from test
group by crop
;
+--------+-------------+--------------+
| CROP | TOTAL_COUNT | GROWERS |
|--------+-------------+--------------|
| maize | 3 | Lulu, Felix |
| apples | 2 | Lulu, Meni |
| cotton | 3 | Delina, Lexi |
+--------+-------------+--------------+
you can use GROUP_CONCAT() or any related fun according to your data base
select "CROP",count(*) "totalCount",GROUP_CONCAT(grower) as growers
from "table"
group by "CROP"
order by "totalCount" desc
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.
So let's say that I have a table, BMST. I use this query to find a result set:
SELECT PARENT,
CHILD,
LEVEL,
QTY
FROM BMST
WHERE PARENT = '111'
In the result set are PARENT part numbers, as well as CHILD part numbers such as:
PARENT | CHILD | LEVEL | QTY
-----------------------------
111 | 222 | 0 | 2
111 | 333 | 0 | 1
111 | 444 | 0 | 1
The table gives information on all the CHILD parts that are used to make the PARENT part, as well as the QUANTITY of CHILD parts used in each PARENT part. The LEVEL column has a value of '0' because part '111' is the origin part that we care about. We do not care if part '111' is a CHILD part to another larger PARENT part.
It is possible for CHILD parts to be PARENT parts as well if they are made up of smaller CHILD parts. For example, this query:
SELECT PARENT,
CHILD,
LEVEL,
QTY
FROM BMST
WHERE PARENT = '222'
would return:
PARENT | CHILD | LEVEL | QTY
-----------------------------
222 | 555 | 1 | 1
222 | 666 | 1 | 1
222 | 777 | 1 | 1
The LEVEL value in this new table is '1' because the part '222' is a CHILD part of a LEVEL = '0' PARENT part.
Going even further, the CHILD parts of part '222' could have CHILD parts themselves, so that a similar query for part '777' would return:
PARENT | CHILD | LEVEL | QTY
-----------------------------
777 | 999 | 2 | 2
My question is, would it be possible to create a query that would return the first result set, and then check all of the CHILD part values within that result set to see if those have any CHILD parts, and then checks the resulting CHILD parts for even more CHILD parts, etc. until there are no more CHILD parts, and then UNION those that do into the first result set so it looks like:
PARENT | CHILD | LEVEL | QTY
-----------------------------
111 | 222 | 0 | 2
222 | 555 | 1 | 1
222 | 777 | 1 | 1
777 | 999 | 2 | 2
222 | 888 | 1 | 1
111 | 333 | 0 | 1
111 | 444 | 0 | 1
The LEVEL value needs to increment for every step deeper that the query goes, and the end result set should show every single part that goes into the requested PARENT part.
Is there a way to do all of this in SQL? Or do I have to use VB6 or another program to iterate through the loops? Any and all feedback is appreciated.
Instead of a union you should look into a SELF JOIN. You can join a table to itself, thereby linking the parent & child IDs. It will require a clause to eliminate rows being joined with themselves. The child ID is essentially the primary key in your example, and the parent ID acts as sort of a foreign key.
Here is an example of this done using Microsoft Access, since it was handy:
Table BMST:
PARENT CHILD LEVEL QTY
111 222 0 2
111 333 0 1
111 444 0 1
222 555 1 1
222 666 1 1
222 777 1 1
555 aaa 2 11
555 aab 2 12
aaa xxx 3 100
aab www 3 111
aaa UUU 3 121
Query:
SELECT c.PARENT, c.CHILD, c.[LEVEL], c.QTY
FROM BMST AS P, BMST AS C
WHERE P.child = C.parent
and P.parent <> C.parent
ORDER BY c.level;
Results:
PARENT CHILD LEVEL QTY
222 777 1 1
222 666 1 1
222 555 1 1
555 aab 2 12
555 aaa 2 11
aaa UUU 3 121
aab www 3 111
aaa xxx 3 100
Note that I invented some additional records to demonstrate that this covers all levels of the hierarchy. The query is imperfect because the topmost parent is excluded (having no parent itself) which probably could be address by using an outer join.
Curiously, the results of this particular example query closely mimics the table itself, but that is before any other criteria is applied, such as which parent element you are really interested in.
This question can provide more information: Explanation of self-joins
To do what you want you will need something called recursion. Ofcourse, you can parse it line by line (with T-SQL, or with VB, or whatever language you are comfortable in), however, this problem (recursion) is very easy to solve with something called Common Table Expressions or CTE.
With a CTE you are able to union against your result, so a PARENT-CHILD relation where the CHILD can be a PARENT is solveable in this case.
I have created this script to show you how. First i populate some Temp tables, after that i am querying using the CTE
if object_id('tempdb..#BMST') is not null
begin
drop table #BMST
end
create table #BMST (
PARENT varchar(5)
, CHILD varchar(5)
, LEVEL varchar(5)
, QTY varchar(5)
)
insert into #BMST
select '111', '222', 0, 2
union all select '111', '333', 0, 1
union all select '111', '444', 0, 1
union all select '222', '555', 1, 1
union all select '222', '666', 1, 1
union all select '222', '777', 1, 1
union all select '777', '999', 2, 2
Blow is the CTE. A Common Table Expression always has to be the first statement, so a semicolon is used for that. After that a with xxx as () construction starts. The results is a fictional name and can be anything.
(In this example i used a new colom SECONDLEVEL to show you the new level)
;with results as (
select *
, 0 as SECONDLEVEL
from #BMST b
union all
select b.*
, r.SECONDLEVEL + 1 as SECONDLEVEL
from #BMST b
inner join results r
on r.CHILD = b.PARENT
and b.LEVEL > r.LEVEL
)
select *
from results
As you can see, i am using an UNION ALL operator. The top part is querying the #temp table, and it the bottom part it is using that to join to results that are already fetched.
And that's it. You have recursion now.
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.
I am trying to get some totals of multiple records based on Location - but get totals for multiple columns to present back - basically there are various locations and each location has different species and totals at that location.
Without doing something really ugly - I have no idea of the best approach - any suggestions?
I have a table
Record
RecordID, Location, Species1, Species2, Species3
1, Loc1, 3, NULL,1
2, Loc2, NULL, 12, NULL
3, Loc2, 2, 2, 2
4, Loc1, 1, 2, 3
5, Loc4, 3, NULL, NULL
I need to get the following data like this:
Location | Species1 | Species2 | Species3
Loc1 | 4 | 2 | 4
Loc2 | 2 | 14 | 2
Loc4 | 3 | NULL | NULL
SELECT Location,
SUM(ISNULL(Species1, 0)) AS Species1,
SUM(ISNULL(Species2, 0)) AS Species2,
SUM(ISNULL(Species3, 0)) AS Species3
FROM Record
GROUP BY Location